OTP主管可以监视远程节点上的进程吗?

Bri*_*ley 5 erlang distributed erlang-otp erlang-supervisor

我想在我正在构建的分布式应用程序中使用erlang的OTP主管。但是我很难弄清这种主管如何监视远程节点上运行的进程。与erlang的start_link函数不同,start_child没有用于指定将在其上生成子节点的Node的参数。

OTP主管可以监视远程孩子吗?如果没有,我如何用erlang实现呢?

zxq*_*xq9 5

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}调用生成特定函数,您正在调用的节点必须有权访问目标模块和函数——如果它已经有代码的副本,为什么它不是调用节点的副本?

希望这个答案解释的比它混淆的要多。