如何从受监督的工作进程触发elixir supervisor树终止

mko*_*zun 5 erlang elixir erlang-otp erlang-supervisor

我试图从受监督的工人流程中终止整个监督树.这是我的监督树:

                   +--------------------------+
                   |                          |
          +--------+ Sup1: Dynamic Supervisor +---------+
          |        |                          |         |
          |        +-------------+------------+         |
          |                      |                      |
          |                      |                      |
          v                      v                      v

+------------------+   +------------------+  +------------------+
|                  |   |                  |  |                  |
| Job1: Supervisor |   | Job2: Supervisor |  | Job3: Supervisor |
|                  |   |                  |  |                  |
+------------------+   +-+-------- +---+--+  +------------------+
                         |             |
                         |             |
                         |             |
                         |             |
                         v             v

             +-------------------+  +--------------+
             |                   |  |              |
             | Progress Monitor: |  | Work: Worker |
             |       Worker      |  |              |
             |                   |  +--------------+
             +-------------------+
Run Code Online (Sandbox Code Playgroud)

流程生命周期:

  1. A Job通过以下方式开始:DynamicSupervisor.start_child(__MODULE__, spec)
  2. 每个工作也是一个监督树:1个主管(重启策略 - one_for_one) - > 2个工人
  3. Progress Monitor 工人知道什么时候完成给定的工作
  4. 完成工作后,Progress Monitor工作人员通过调用以下方法尝试终止整个工作监督树:DynamicSupervisor.terminate_child(__MODULE__, pid)
  5. Progress Monitor预计会在terminate回调中执行清理步骤- 它会捕获退出信号

问题和意见:

  1. DynamicSupervisor.terminate_child 是一个阻塞调用,这意味着它等待所有子进程也终止,包括调用进程 - Progress Monitor
  2. Progress Monitor陷入僵局,无法终止.父监督发送:kill信号,不触发terminate回叫

快速解决方法:

  1. DynamicSupervisor.terminate_childProgress Monitor工作人员异步调用:

    spawn(fn -> DynamicSupervisor.terminate_child(__MODULE__, pid) end)

  2. 定义关闭策略Sup1: Dynamic Supervisor:

    shutdown: 5_000

    它将等待最多5秒的工作监督树终止,然后它将发送shutdown退出信号.这将确保terminateProgress Monitor进程调用回调.

对他们两个都不满意.

问题:

  1. 如何从工作进程触发监督树终止并避免死锁?
  2. 如果终止工人的监督树不是最佳做法,那么推荐的方式是什么呢?
  3. 任何建议如何重新设计监督树,使优雅的终止更容易?

Mil*_*ric 1

只需在异步任务中调用它,Task.async(fn -> Process.exit(Sup1, :shutdown) end)它就会终止 Sup1,并且所有子任务都会关闭

编辑:

如果您需要更漂亮的解决方案,这取决于您还需要什么。在大多数情况下,我创建 Bootstrapper 工作程序来执行初始化和其他一些操作。您可以轻松添加其他功能。

因此,考虑到上面的内容,粗略地说,我会在上面添加一个层 ( AppSupervisor),另一个 DynamicSupervisor,以便它可以启动 Bootstrapper 并传递self()给它(或在本地名称下注册它以避免这种注入)。之后,在启动时,Bootstrap Worker 将启动 Sup1(您的动态管理程序)并等待其他消息,例如,:terminate_sup1将关闭Sup1进程。稍后,在下面的一些工作程序中,您可以Sup1通过将:terminate_sup1消息传递给引导程序来关闭。还有一扇门允许您在另一条消息发送到引导工作程序时重新启动 Sup1。

此外,如果您只需要关闭 Sup1,只需使用 Task 即可。但如果您需要控制,则将其放入单个工作进程中,该进程应在​​其启动或关闭时对其进行控制。