如何从受监督的工作进程中触发 Elixir 主管树终止
Posted
技术标签:
【中文标题】如何从受监督的工作进程中触发 Elixir 主管树终止【英文标题】:How to trigger elixir supervisor tree termination from a supervised worker process 【发布时间】:2019-04-17 07:02:55 【问题描述】:我正在尝试从受监督的工作进程中终止整个监督树。这是我的监督树:
+--------------------------+
| |
+--------+ Sup1: Dynamic Supervisor +---------+
| | | |
| +-------------+------------+ |
| | |
| | |
v v v
+------------------+ +------------------+ +------------------+
| | | | | |
| Job1: Supervisor | | Job2: Supervisor | | Job3: Supervisor |
| | | | | |
+------------------+ +-+-------- +---+--+ +------------------+
| |
| |
| |
| |
v v
+-------------------+ +--------------+
| | | |
| Progress Monitor: | | Work: Worker |
| Worker | | |
| | +--------------+
+-------------------+
流程生命周期:
Job
通过以下方式启动:DynamicSupervisor.start_child(__MODULE__, spec)
每个作业也是一个监督树:1 个主管(重启策略 - one_for_one
)-> 2 个工人
Progress Monitor
worker 知道给定工作何时完成
工作完成后,Progress Monitor
worker 尝试终止整个工作监督树,方法是调用:DynamicSupervisor.terminate_child(__MODULE__, pid)
Progress Monitor
预计会在 terminate
回调中执行清理步骤 - 它正在捕获退出信号
问题和观察:
DynamicSupervisor.terminate_child
是一个阻塞调用,这意味着它也等待所有子进程终止,包括调用进程 - Progress Monitor
Progress Monitor
处于死锁状态,无法终止。父supervisor发送:kill
信号,不会触发terminate
回调
快速解决方法:
从Progress Monitor
worker 异步调用DynamicSupervisor.terminate_child
:
spawn(fn -> DynamicSupervisor.terminate_child(__MODULE__, pid) end)
为Sup1: Dynamic Supervisor
定义关闭策略:
shutdown: 5_000
它将最多等待 5 秒等待作业监督树终止,然后发送shutdown
退出信号。这将确保为Progress Monitor
进程调用terminate
回调。
对他们俩都不满意。
问题:
-
如何从工作进程触发监督树终止并避免死锁?
如果从工作人员处终止监督树不是最佳做法,那么推荐的方法是什么?
有什么建议可以重新设计监督树以使优雅终止更容易吗?
【问题讨论】:
在我看来Progress Monitor
是不必要的。你能解释为什么Worker
不能简单地做它的事情然后以normal
的原因终止吗?看来您可以为此目的使用 Task.Supervisor
。
【参考方案1】:
只需在异步任务Task.async(fn -> Process.exit(Sup1, :shutdown) end)
中调用它,它将终止 Sup1,并且所有子进程都将关闭
编辑:
如果您需要更漂亮的解决方案,这取决于您还需要什么。在大多数情况下,我会创建 Bootstrapper 工作程序来进行初始化和其他一些事情。您可以轻松添加其他功能。
所以考虑到上面,粗略地说,我会在上面添加一个层(AppSupervisor
),另一个 DynamicSupervisor 以便它可以启动 Bootstrapper 并将self()
传递给它(或以本地名称注册它以避免这种注入)。之后,在启动时,Bootstrap worker 将启动 Sup1(你的动态主管)并等待其他消息,例如:terminate_sup1
将关闭 Sup1
进程。稍后,在下面的一些工作人员中,您可以通过将 :terminate_sup1
消息投射到引导程序来关闭 Sup1
。当另一条消息发送到引导工作程序时,还有一个允许您重新启动 Sup1 的门。
此外,如果您只需要关闭 Sup1,只需使用 Task。但是,如果您需要控制,则将其放入应该可以控制它的单个工作进程中,无论何时启动或关闭。
【讨论】:
以上是关于如何从受监督的工作进程中触发 Elixir 主管树终止的主要内容,如果未能解决你的问题,请参考以下文章