了解 Erlang/Elixir 中的主管职责

Understanding supervisor duty in Erlang/Elixir

我写了一个名为 director 的新库。
这是一个主管图书馆。
它的一个特点是给 director 一个 arity 2 的乐趣,director 会在每次进程崩溃时调用函数,第一个参数是崩溃原因,第二个是崩溃计数,例如:

-module(director_test).
-behaviour(director).
-export([start_link/0, init/1]).

start_link() ->
    director:start_link(?MODULE, []).

init([]) ->
    ChildSpec = #{id => foo,
                  start => {m, f, args},
                  plan => [fun my_plan/2],
                  count => infinity},
    {ok, [ChildSpec]}.

my_plan(normal, Count) when Count rem 10 == 0 ->
    %% If process crashed with reason normal after every 10 times
    %%, director will restart it after spending 3000 milliseconds.
    {restart, 3000};
my_plan(normal, _Count) ->
    %% If process crashed with reason normal director will restart its
    restart;
my_plan(killed, _Count) ->
    %% If process was killed, Director will delete it from its children
    delete;
my_plan(Reason, Count) ->
    %% For other reasons, director will crash with reason {foo_crashed, Reason}
    {stop, {foo_crashed, Reason}}.

我在 Slack 中宣布了我的图书馆,他们想知道以这种方式编写新主管! 有人说 "I tend to not let the supervisor handle back-off".
最后他们没有告诉我干净的信息,我想我需要更多地了解主管及其职责等。 我认为主管是一个进程,应该明白什么时候重启哪个child,什么时候删除哪个child,什么时候不重启哪个child。 我说得对吗?

你能告诉我一些 OTP/Supervisor 的好的功能是我在 Director 中没有的吗? (List of director's features)

请不要把这当成个人的。您已要求提供反馈,我正在尝试将其提供给您。

快速查看文档和代码后,我认为您的库的主要问题是:

  1. 您在通常不需要的地方引入了一些复杂性。在绝大多数 Erlang 程序中,您不想分析进程崩溃的原因。分析它很容易出错。所以"normal"的解决办法就是重启进程。如果您此时引入任何逻辑,您可能也会引入一些错误。这样的程序更难推理,至少优点是有争议的。

  2. 您假设退出原因是 进程 退出的原因。这不一定是真的。原因可能是从其链接的进程传播的。如果您想真正对 所有 可能的退出原因做出反应,则必须对所有进程退出原因、所有子进程退出原因、所有子进程退出原因等进行传递闭包。并且每当任何组件发生变化时,您都必须更改它,这是非常糟糕的态度,非常容易出错。并且引入的复杂性(见 1)爆炸得非常厉害。

  3. 你在内部逻辑应该保持理想状态的上下文中引入了一些"introspection"逻辑——也就是说,有一些关于在其模块外部使用的进程的内部工作的知识——在导演的计划。这在某种程度上破坏了封装。 "normal" supervisor 只知道如何启动进程,它不需要任何关于进程内部的更多信息。

  4. 最后但同样重要的是:您可能正在解决一个不存在的问题。与其开发一个全新的解决方案,不如清楚地识别现有解决方案的问题,并尝试非常直接和最少地解决它们。

你混合了监督管理的想法。

监督 已经是 OTP 的一部分。基本思路是:

  • 任何进程都不可能成为孤儿
  • 崩溃将重新启动或中止,这是在编写内部逻辑之前做出的架构决定。
  • 崩溃可以在外部记录(由失败的进程处理)。
  • 错误处理代码、崩溃取证等永远不会作为监督的一部分发生。曾经。 (复杂的逻辑导致复杂的怪异,监管需要简单、稳健、可靠。)

Management 是您的系统中可能存在也可能不存在的东西,因此由您决定。它的想法是您将有一个单一的(通常是命名的)流程来指导您的(受监督的)工作人员正在执行的总体高级任务。拥有一个管理器流程可以让您对正在完成的整体工作进行单点控制——这也意味着它是一个单一的地方,您可以告诉整体工作开始、停止、暂停等,这是您可以添加的地方关于基于某些崩溃条件选择性重启的附加逻辑。

将 "supervision" 视为低级系统框架类型的想法。它在所有程序中总是相同的,就像打开文件或处理网络套接字一样。将管理视为 您的程序需要解决 以完成其工作的实际问题的一个离散部分。

管理可能复杂也可能不复杂。监督必须始终统一和简单。赋予主管过多的责任会使他们难以理解和调试,并且经常会导致业务问题——超负荷的主管可能是系统中的主要问题。不要让你的主管承担高级别的管理任务。

前阵子我在 Erlang 中写了一篇关于 "service -> worker pattern" 的文章。希望它能提供更多信息而不是混淆:https://zxq9.com/archives/1311