我想在我正在构建的分布式应用程序中使用 erlang 的 OTP 主管。但我无法弄清楚这种主管如何监控远程节点上运行的进程。与 erlang 的 start_link 函数不同,start_child 没有用于指定将在其上生成子节点的参数。
OTP 主管是否可以监控远程 child ,如果没有,我如何在 erlang 中实现这一点?
最佳答案
supervisor:start_child/2
可以跨节点使用。
您感到困惑的原因只是对执行上下文的混淆(诚然,有时很难保持直截了当)。任何 OTP 生成都涉及三个进程:
请求者的上下文是调用
supervisor:start_child/2
的上下文——而不是主管本身的上下文。您通常会通过导出一个将调用包装到 supervisor:spawn_child/2
的函数来提供主管接口(interface):do_some_crashable_work(Data) ->
supervisor:start_child(sooper_dooper_sup, [Data]).
这可以从主管模块定义和导出,根据 "service manager/supervisor/workers" idiom 在“管理器”类型的进程内部定义,或其他。但是,在所有情况下,除了主管之外的某些进程正在发出此调用。
现在再次仔细查看
supervisor:start_child/2
的 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
的所有内容是为了什么。事实证明,虽然从多节点系统中的任何位置调用生成以在多节点系统中的任何其他位置启动生成是可能的,但这并不是一种非常有用的业务方式,因此 gen_*
行为甚至 proc_lib:start_link/N
也没有一种方法来声明在其上生成新进程的节点。理想情况下,您需要知道如何在运行后自行初始化并加入集群的节点。您的系统提供的任何服务通常最好复制到集群中的其他节点上,然后您只需编写一种选择节点的方法,这样您就可以完全完成启动业务,因为它现在是节点本地的每一个案例。在这种情况下,无论您的普通经理/主管/工作人员代码所做的任何事情都不必更改 - 事情只是发生了,请求者的 PID 恰好在另一个节点上并不重要,即使该 PID 是必须返回哪些结果。
换句话说,我们真的不想在任意节点上产生工作人员,我们真正想做的是升级到更高的级别,并要求另一个节点完成一些工作,而不是真正关心它是如何发生的。请记住,要基于
{M,F,A}
调用生成特定函数,您正在调用的节点必须能够访问目标模块和函数——如果它已经有代码的副本,为什么它不是调用节点的副本?希望这个答案能解释更多而不是混淆。
关于erlang - OTP 主管可以监控远程节点上的进程吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40791674/