如何从受监督的工作进程中触发Elixir主管树终止

时间:2018-11-14 10:31:34

标签: erlang elixir otp supervisor

我正在尝试从受监督的工作进程中终止整个监督树。这是我的监督树:

... // (as in your code: p & end exist already, key & p_ not yet)

auto [key, p_] = load_string(p, end);
p = move(p_);

... // (continue using p)

流程生命周期:

  1. 通过以下方式启动 +--------------------------+ | | +--------+ Sup1: Dynamic Supervisor +---------+ | | | | | +-------------+------------+ | | | | | | | v v v +------------------+ +------------------+ +------------------+ | | | | | | | Job1: Supervisor | | Job2: Supervisor | | Job3: Supervisor | | | | | | | +------------------+ +-+-------- +---+--+ +------------------+ | | | | | | | | v v +-------------------+ +--------------+ | | | | | Progress Monitor: | | Work: Worker | | Worker | | | | | +--------------+ +-------------------+
  2. 每项工作也是一个监督树:1名主管(重新启动策略-Job)-> 2名工人
  3. DynamicSupervisor.start_child(__MODULE__, spec)工作者知道给定工作的完成时间
  4. 完成工作后,one_for_one工作人员尝试通过调用Progress Monitor
  5. 来终止整个工作监督树。
  6. Progress Monitor有望在DynamicSupervisor.terminate_child(__MODULE__, pid)回调中执行清理步骤-它正在捕获退出信号

问题和发现:

  1. Progress Monitor是阻塞调用,这意味着它也等待所有子进程也终止,包括调用进程-terminate
  2. DynamicSupervisor.terminate_child处于死锁状态,无法终止。上级主管发送Progress Monitor信号,但不会触发Progress Monitor回调

快速解决方法:

  1. :kill工作人员异步调用terminate

    DynamicSupervisor.terminate_child

  2. Progress Monitor定义关闭策略:

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

    它将最多等待5秒钟以等待作业监督树终止,然后将发送Sup1: Dynamic Supervisor退出信号。这将确保shutdown: 5_000进程被调用shutdown回调。

对他们两个都不满意。

问题:

  1. 如何从工作进程中触发监视树终止并避免死锁?
  2. 如果终止工人的监督树不是最佳实践,那么推荐的方法是什么?
  3. 有没有建议如何重新设计监督树以简化终止程序?

1 个答案:

答案 0 :(得分:1)

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

编辑:

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

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

此外,如果您只需要关闭Sup1,请使用Task。但是,如果您需要控制,则可以将其置于应该控制它的单个工作进程中,无论它处于启动还是关闭状态。