在我的Erlang / OTP应用程序中,我有一个one_for_all
主管( sup ),其中有几个孩子。其中一个孩子( child1 ,gen_server
行为)应该能够向另一个孩子发送消息( child2 ,行为为supervisor
)。当然,我可以注册它,但用多余的名字堵塞全球范围似乎不是一个好主意。
因此,使这种交互成为可能的唯一方法是使用 child2 的pid提供 child1 。说到做到。有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设计原则,监管树成员之间最常见的交互方式是什么?
答案 0 :(得分:5)
当然,你还不能向主管询问有关其孩子的问题,但还没有启动它们:)
实际上,注册(在本地,使用erlang:register())并不是一个坏主意。此外,如果在child1中处理原始pid,你应该手动设置child2 pid的监控,以便能够对可能的崩溃等作出反应,但是注册后你就可以直接通过名字来询问。
如果没有注册,您可以推迟通知孩子直到主管:start_link被调用:
start_link() ->
R=supervisor:start_link({local, ?SERVER}, ?MODULE, []),
%% Here supervisor is started so you can notify its children
R.
答案 1 :(得分:1)
这很大程度上取决于您的业务逻辑,但如果您担心会堵塞全球范围,我的建议是您考虑使用的监管策略。
在这种情况下,当两个对等进程需要以这种方式关联时,我看到的是创建了一个额外的控制器进程来管理这种关联,而simple_one_for_one支持用于生成两个对等进程之一(gen_server最有可能)。当使用进程注册时,可以再次使用此技术,例如当您将有许多运行相同代码的gen_servers实例时。
该技术基本上包括使用simple_one_for_one管理程序来生成gen_server。基本上发生的事情是,启动时(simple_one_for_one sup)不会立即生成任何子进程,但只有在您明确调用supervisor:start_child(Sup, List)
时才会生成。
然后你的child2初始化逻辑(甚至你的业务逻辑在某一点上)可以通过supervisor:start_child(Sup, List)
生成gen_server并被child2 Pid所提供,这样他们就可以毫不费力地进行通信。
额外的控制器进程保留了gen_server之间关系的字典,由一些唯一性标识,但避免了全局空间堵塞,因为这只是控制器的本地空间。然后,您可以要求控制器在逻辑上的任何位置分配或取消分配此关系。
监督树因此看起来像:
您可以在github上的tinymq项目中使用此实现读取代码
如果您以前从未使用过simple_one_for_one主管,那么子规范可能有点棘手,请记住,您可以通过supervisor:start_child(Sup, List)
上的{{1}}列表将child2的pid传递给gen_server。 Args one,您在子规范中指定的。
simple_one_for_one supervisors
我的两分钱!