【问题标题】:Supervisor.restart_child/2 or Process.exit(pid, :kill)?Supervisor.restart_child/2 还是 Process.exit(pid, :kill)?
【发布时间】:2019-01-10 02:43:56
【问题描述】:

我有一个监督树,为了简单起见,假设一名监督员 (S) 持有一名工作人员 (W),策略为 :one_for_one

在某些情况下,我需要重新初始化W,而最简单的方法就是让它崩溃。我有两种不同的选择:Process.exit/2 和/或Supervisor.restart_child/2

Process.exit(W, :kill)

[{id, _, :worker, _}] = Supervisor.which_children(S)
Supervisor.terminate_child(S, w_id)
Supervisor.restart_child(S, w_id)

既然存在后者,我想它可能会更好用,但我不知道使用它会有什么好处。假设将针对策略:rest_for_one 和许多孩子,前者将重新启动工人列表的整个尾部,而后者将仅重新启动该特定工人。我找不到任何合理的文档,也没有在代码库中发现这种差异。

所以,问题是:当使用:one_by_one 策略时,通过terminaterestart 循环或Process.exit(pid, :kill) 是否足够?

【问题讨论】:

    标签: elixir erlang-supervisor


    【解决方案1】:

    除了工作人员在使用Process.exit/2 时对退出信号有发言权,这可能与您的场景不完全相关(尽管可能会挂钩退出信号陷阱所需的任何重新初始化以避免重新启动工作人员),这里可能是相关的。

    方法 A。当Process.exit(W, :kill) 被调用时,会发生以下情况:

    1. 工作进程被终止
    2. 主管 receives 'EXIT' 来自已终止工作人员的信号
    3. 主管调用restart_child/3,它会根据指定的重启策略执行此操作

    非常精瘦和刻薄。

    方法 B。使用另一种手动方法会发生以下情况:

    1. terminate_child/2 最终调用 shutdown/2
    2. shutdown/2 默认情况下会尝试优雅地关闭子进程,使其有机会释放任何系统资源
    3. 如果孩子在超时后没有退出,它就会被杀死
    4. 由于childgets unlinked在被关闭之前,supervisor没有收到'EXIT'信号,也不会自动重启child
    5. 下一个调用 restart_child(S, w_id) 重新启动子进程,重用其规范并规避重启策略

    为了适用方法A,儿童不得分配外部资源。使用one_for_one 策略,它是一个不错的捷径,在其约束范围内很有用。使用其他策略会导致其他孩子可能无用和/或昂贵的重新启动。当约束不是问题时,这种方法可能是对稳定解决方案的合理优化。

    方法 B 是一种更通用的方法来控制单个子项重新启动。它确实涉及更复杂的逻辑,以提供优雅的行为作为交换。它还允许在子终止和重新启动之间放置额外的逻辑。额外的好处是即使使用rest_for_oneone_for_all 策略也能精确地重新启动目标子节点。在我看来,对于不断发展的应用来说,这是一个更好的选择,因为它不受特定约束的限制,可以更轻松地更改实施。

    【讨论】:

    • 好的,这是正确的,并且在问题本身中已说明。您确定对于 one_for_rest 策略它不会重新启动后续子项吗?如果是,我自己在哪里可以保证?
    • 是的,因为策略定义的重启是由restart_child/3 调用的,它在L854 调用restart。它仅从'EXIT' 信号处理程序调用,并且该信号由于取消链接而未到达。相反,您调用 restart_child/2 获取现有规范并调用 do_start_child 而无需查看重启策略。
    • 后面有ErlangVM。这可能会引起注意。
    • 其他一切都在supervisor.erl 中显式编程,我不希望这样。当然,也可以进行测试。
    猜你喜欢
    • 2013-01-30
    • 1970-01-01
    • 1970-01-01
    • 2016-05-02
    • 2017-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-28
    相关资源
    最近更新 更多