Bri*_*ley 5 erlang distributed erlang-otp erlang-supervisor
我想在我正在构建的分布式应用程序中使用erlang的OTP主管。但是我很难弄清这种主管如何监视远程节点上运行的进程。与erlang的start_link函数不同,start_child没有用于指定将在其上生成子节点的Node的参数。
OTP主管可以监视远程孩子吗?如果没有,我如何用erlang实现呢?
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]).
Run Code Online (Sandbox Code Playgroud)
这可能是从主管模块定义和导出的,根据“服务经理/主管/工人”习语或其他方式在“经理”类流程中内部定义。但是,在所有情况下,除主管之外的某些进程都会发出此调用。
现在supervisor:start_child/2
再次仔细查看 Erlang 文档(这里和R19.1 文档镜像,因为有时 erlang.org 出于某种原因遇到困难)。请注意,类型sup_ref()
可以是注册名称、 a pid()
、 a{global, Name}
或 a {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]).
Run Code Online (Sandbox Code Playgroud)
带有类似以下内容的子规范:
%% Usually in the supervisor code
SooperWorker = {sooper_worker,
{sooper_worker, start_link, []},
temporary,
brutal_kill,
worker,
[sooper_worker]},
Run Code Online (Sandbox Code Playgroud)
这表明第一个调用将是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).
Run Code Online (Sandbox Code Playgroud)
你可能想知道这一切proc_lib
是为了什么。事实证明,虽然从多节点系统内的任何地方调用 spawn 以在多节点系统内的任何其他地方启动 spawn 是可能的,但这并不是一种非常有用的业务方式,因此gen_*
行为和甚至proc_lib:start_link/N
没有声明在其上产生新进程的节点的方法。
理想情况下,您想要的是知道如何初始化自己并在运行后加入集群的节点。您的系统提供的任何服务通常最好复制到集群内的其他节点上,然后您只需编写一种选择节点的方法,这使您可以完全执行启动业务,因为它现在是节点本地的每一个案例。在这种情况下,无论您的普通经理/主管/工人代码做什么,都不必更改 - 事情只是发生了,并且请求者的 PID 恰好在另一个节点上并不重要,即使该 PID 是要发送的地址必须返回哪些结果。
换句话说,我们真的不想在任意节点上生成工作线程,我们真正想做的是提升到更高的级别并请求其他节点完成某些工作,而不是真正关心这是如何发生的。请记住,要根据{M,F,A}
调用生成特定函数,您正在调用的节点必须有权访问目标模块和函数——如果它已经有代码的副本,为什么它不是调用节点的副本?
希望这个答案解释的比它混淆的要多。