我想在我正在构建的分布式应用程序中使用erlang的OTP管理程序。但是我很难弄清楚这种主管如何监控在远程节点上运行的进程。与erlang的start_link函数不同,start_child没有用于指定将在其上生成子节点的Node的参数。
OTP主管是否可以监控远程孩子,如果没有,我怎样才能在erlang中实现这一点?
答案 0 :(得分:3)
supervisor:start_child/2
可以跨节点使用。
你混淆的原因只是对执行环境的混淆(有时候有点难以保持直线)。任何OTP产卵都涉及三个过程:
请求者的上下文是supervisor:start_child/2
被调用的 - 不主管本身的上下文。您通常会通过导出一个将调用包装到supervisor:spawn_child/2
:
do_some_crashable_work(Data) ->
supervisor:start_child(sooper_dooper_sup, [Data]).
可以从管理程序模块定义和导出,可以根据"service manager/supervisor/workers" idiom或其他任何内容在“管理器”类型的流程内部定义。但是,在所有情况下,除了主管之外的某些进程正在进行此调用。
现在再次仔细查看supervisor:start_child/2
的{{1}}的Erlang文档(here和an R19.1 doc mirror,因为有时候erlang.org由于某种原因很难)。请注意,sup_ref()
类型可以是注册名称,pid()
,{global, Name}
或{Name, Node}
。在使用pid()
,{global, Name}
或{Name, Node}
元组进行呼叫时,请求者可能在任何其他节点上呼叫主管的节点上。
但是,主管并不只是随意开球。它有一个child_spec()
它正在关闭,规范告诉主管要调用什么来启动这个新进程。第一次调用子模块是在主管的上下文中进行的,并且是一个自定义函数。虽然我们通常将其命名为start_link/N
,但它可以作为启动的一部分执行任何操作,包括声明要生成的特定节点。所以现在我们结束这样的事情:
%% Usually defined in the requestor or supervisor module
do_some_crashable_work(SupNode, WorkerNode, Data) ->
supervisor:start_child({sooper_dooper_sup, SupNode}, [WorkerNode, Data]).
有一个类似的孩子规格:
%% Usually in the supervisor code
SooperWorker = {sooper_worker,
{sooper_worker, start_link, []},
temporary,
brutal_kill,
worker,
[sooper_worker]},
这表明第一个电话会是sooper_worker:start_link/2
:
%% The exported start_link function in the worker module
%% Called in the context of the supervisor
start_link(Node, Data) ->
Pid = proc_lib:spawn_link(Node, ?MODULE, init, [self(), Data]).
%% The first thing the newly spawned process will execute
%% in its own context, assuming here it is going to be a gen_server.
init(Parent, Data) ->
Debug = sys:debug_options([]),
{ok, State} = initialize_some_state(Data)
gen_server:enter_loop(Parent, Debug, State).
你可能想知道proc_lib
的所有问题是什么。事实证明,当从多节点系统中的任何地方调用spawn来启动多节点系统中其他地方的spawn时,可能,它只是不是一个非常有用的方法业务,因此gen_*
行为甚至proc_lib:start_link/N
都没有声明生成新流程的节点的方法。
理想情况下,节点知道如何初始化自身并在群集运行后加入群集。您的系统提供的任何服务通常最好在群集中的其他节点上进行复制,然后您只需编写一种选择节点的方法,这样您就可以完全理解启动业务,因为它现在是节点本地的每个案例。在这种情况下,无论你的普通经理/主管/工人代码做什么都不需要改变 - 事情刚刚发生,并且请求者的PID恰好在另一个节点上并不重要,即使该PID是地址到必须返回哪些结果。
换句话说,我们真的不想在任意节点上产生工作者,我们真正想做的是升级到更高级别并且请求另一个节点完成某些工作并没有真正关心这是怎么发生的。请记住,要根据{M,F,A}
调用生成特定函数,您调用的节点必须能够访问目标模块和函数 - 如果它已经拥有代码的副本,为什么它不是重复的主叫节点?
希望这个答案解释的不仅仅是困惑。