7

在我的 Erlang/OTP 应用程序中,我有一个one_for_all主管 ( sup ) 和几个孩子。其中一个孩子(有行为的child1gen_server)应该能够向另一个孩子(有行为的child2supervisor )发送消息。当然,我可以注册它,但用多余的名称堵塞全局范围似乎不是一个好主意。

因此,使这种交互成为可能的唯一方法是为 child1 提供child2pid。说到做到。有supervisor:wich_children/1适当功能的调用。只需将sup的 pid 作为参数传递给chidl1,调用which_children,child1:init并... 陷入僵局。sup正在等待child1开始,child1正在等待sup子描述:

init(SupPid) ->
    Descriptions = supervisor:which_children(SupPid),
    ... .

这可以通过以下方法解决:

init(SupPid) ->
    gen_server:cast(self(), initialize),
    ... .

handle_cast(initialize, State) ->
    Descriptions = supervisor:which_children(SupPid),
    ...  % Generating new state containing desired pid
    {noreply, NewState}.

但是,我对这个解决方案并不满意。

问题是:根据 OTP 设计原则,监督树成员之间最常规的交互方式是什么?

4

2 回答 2

5

当然,您不能在主管尚未启动所有子项时向主管询问其子项:)

实际上,注册(在本地,使用 erlang:register())并不是一个坏主意。此外,如果在 child1 中处理原始 pid,您应该手动设置对 child2 pid 的监控,以便能够对可能的崩溃等做出反应,但是注册后您只需直接按名称询问即可。

如果没有注册,你可以推迟通知孩子,直到 supervisor:start_link 被调用:

start_link() ->
    R=supervisor:start_link({local, ?SERVER}, ?MODULE, []),

    %% Here supervisor is started so you can notify its children
    R.
于 2012-11-22T23:41:56.277 回答
1

这在很大程度上取决于您的业务逻辑,但如果担心阻塞全局范围对您来说是有意义的,我的建议是您考虑您正在使用的监督策略。

当两个对等进程需要以这种方式关联时,我在这种情况下看到的是,创建了一个额外的控制器进程来管理这种关联,并使用 simple_one_for_one sup 来生成两个对等进程之一(gen_server最有可能的)。当使用进程注册是一个问题时,可以再次使用这种技术,例如当您将有许多运行相同代码的 gen_servers 实例时。

该技术基本上包括使用 simple_one_for_one 主管来生成您的 gen_server。基本上发生的情况是,当启动时(simple_one_for_one sup)不会立即产生任何子进程,但只有在您明确调用supervisor:start_child(Sup, List).

然后,您的 child2 初始化逻辑(甚至在某些时候您的业务逻辑)可以通过 生成 gen_serversupervisor:start_child(Sup, List)并提供 child2 Pid,以便他们可以轻松地进行通信。

额外的控制器进程保留了您的 gen_server 之间的关系的字典,由一些唯一性标识,但避免了全局空间阻塞,因为这只是控制器的本地。然后,您可以要求您的控制器在您的逻辑上的任何时候分配或取消分配此关系。

因此,监督树看起来像:

监督树

您可以在 github上的tinymq项目中使用此实现来阅读代码

如果您以前从未使用过 simple_one_for_one 主管,则子规范可能有点棘手,请记住,您将通过supervisor:start_child(Sup, List)附加到 Args 的列表将 child2 的 pid 传递给您的 gen_server,您在儿童规格

simple_one_for_one 主管

我的两分钱!

于 2012-11-23T17:45:00.913 回答