2

在浏览 erlang 应用程序的代码时,我遇到了一个有趣的设计问题。让我描述一下情况,但由于 PIA 抱歉,我无法发布任何代码。

该代码被构造为一个 OTP 应用程序,其中两个gen_server模块负责分配某种资源。该应用程序在一段时间内完美运行,我们并没有真正遇到大问题。

当第一个gen_server需要检查第二个是否有足够的资源时,棘手的部分就开始了。Acall被发布到第二个 gen_server,它本身调用一个实用程序库(在非常非常特殊的情况下)call向第一个 gen_server 发布 a。

我对 erlang 比较陌生,但我认为这种情况会让两个 gen_server 互相等待。

这可能是一个设计问题,但我只是想知道 OTP 中是否有任何特殊机制可以防止这种“挂起”。

任何帮助,将不胜感激。

编辑:总结答案:如果您遇到两个彼此循环的情况,您最好在应用程序设计中花费更多时间gen_servercall

谢谢你的帮助 :)

4

3 回答 3

4

这称为死锁,可以/应该在设计级别避免。以下是一种可能的解决方法和一些主观观点,希望能帮助您避免犯错。

虽然有很多方法可以解决您的问题,但“等待”正是我们call正在做的事情。

一种可能的解决方法是从 A 内部生成一个调用 B 的进程,但不会阻止 A 处理来自 B 的调用。该进程将直接回复调用者。

在服务器 A 中:

handle_call(do_spaghetti_call, From, State) ->
    spawn(fun() -> gen_server:reply(From, call_server_B(more_spaghetti)) end),
    {noreply, State};
handle_call(spaghetti_callback, _From, State) ->
    {reply, foobar, State}

在服务器 B 中:

handle_call(more_spaghetti, _From, State) ->
    {reply, gen_server:call(server_a, spaghetti_callback), State}

对我来说,这是非常复杂且难以推理的。我认为您甚至可以将其称为意大利面条代码而不会冒犯任何人。

另一方面,虽然上述方法可能会解决您的问题,但您应该认真思考这样的调用实际上意味着什么。例如,如果服务器 A 多次执行此调用会发生什么?如果在任何时候超时会发生什么?您如何配置超时以使其有意义?(最里面的调用必须比外部调用有更短的超时时间,等等)。

我会改变设计,即使它很痛苦,因为当你允许它存在并解决它时,你的系统变得非常难以推理。恕我直言,复杂性是万恶之源,应该不惜一切代价避免。

于 2011-05-17T21:19:35.220 回答
3

这主要是一个设计问题,您需要确保没有来自 gen_server1 的长时间阻塞调用。这可以很容易地通过产生一个小乐趣来完成,它负责您对 gen_server2 的调用,并在完成后将结果传递给 gen_server1。

您必须跟踪 gen_server1 正在等待来自 gen_server2 的响应这一事实。可能是这样的:

handle_call(Msg, From, S) ->
  Self = self(),
  spawn(fun() ->
    Res = gen_server:call(gen_server2, Msg),
    gen_server:cast(Self, {reply,Res})
  end),
{noreply, S#state{ from = From }}.

handle_cast({reply, Res}, S = #state{ from = From }) ->
  gen_server:reply(From, Res),
  {noreply, S#state{ from = undefiend}.

这样 gen_server1 可以在不挂起的情况下为来自 gen_server2 的请求提供服务。当然,您还需要对小过程进行适当的错误传播,但您会明白总体思路。

于 2011-05-17T21:18:53.417 回答
1

另一种我认为更好的方法是使此(资源)信息传递asynchronousmy_resource_state当每个服务器从另一台服务器获得(异步)消息时,它都会做出反应并做它应该做的事情。它还可以通过send_me_your_resource_state异步消息提示其他服务器发送其资源状态。由于这两个消息都是异步的,它们永远不会阻塞,并且服务器可以在提示后等待my_resource_state来自其他服务器的消息时处理其他请求。

消息异步的另一个好处是服务器可以在他们认为有必要时发送此信息而无需提示,例如“帮助我,我的运行速度非常低!” 或“我溢出来了,你想要一些吗?”。

@Lukas 和 @knutin 的两个回复实际上是异步执行的,但它们是通过生成一个临时进程来完成的,该进程可以在不阻塞服务器的情况下进行同步调用。直接使用异步消息更容易,意图也更清晰。

于 2011-05-19T16:18:04.423 回答