【问题标题】:Automatic restarting of dynamically added children of a supervisor自动重新启动主管的动态添加的孩子
【发布时间】:2016-04-10 10:30:06
【问题描述】:

根据 Erlang/OTP 手册,如果我使用 supervisor:start_child 将子级添加到主管并且 supervisor 崩溃,则不会自动重新启动子级。

(http://www.erlang.org/doc/design_principles/sup_princ.html#id73986)

是否有一个简单的解决方法,或者我必须以某种方式手动保存动态添加的孩子并自行管理重新启动? (我的主管上面有一个主管,所以这是可能的,虽然不是很优雅。)

【问题讨论】:

    标签: erlang erlang-otp


    【解决方案1】:

    您的主管和其他任何人一样是流程,不同之处在于它们是系统流程(所有trap_exit 业务)。当它死去时,它所持有的内部状态也会随之消失——噗!

    这是一件好事

    主管死亡的恢复与工人死亡的恢复相同(毕竟,主管是另一个主管的工人)。您可能遇到的情况是您的监督树的 结构 与您的需求不太一致。如果您需要工作在他们的主管去世时继续存在,那么这些任务比您放置它们更接近程序的崩溃核心 - 这意味着它们应该是更高链条的孩子(更有可能)你高估了它们对系统的重要性。

    我开始担心确保流程持续存在的最常见原因是我赋予了它太多责任。每当我发现自己在问诸如“如果主管崩溃时我如何确保孩子由主管重新启动”之类的问题时,我会停下几分钟,仔细考虑我为什么要问这个问题——这总是 让我发现了一个架构问题(并且总是偶然地修复它,让系统的其他部分变得更明智)。

    现实生活中的例子:

    在业务服务器中有一个流程模块,最初是“只是客户端连接”。它发展成为管理与客户端的网络连接、内部 Erlang 值和外部协议值之间的转换,以及表示客户端在系统中的存在(存在、审计活动、授权、聊天等)。因为审计日志,我开始想和你一样的事情:如果 sup 死了,我怎么能关闭客户端接触的东西等等?

    然后显然被忽略的烦人事情发生了:从多个设备同时登录成为一项要求。当有多个相同的“客户端”时,多设备登录很奇怪,依此类推(而不是单个客户端进程使用多个连接进程)。将这些任务分成不同的进程(不仅仅是模块)极大地简化了事情,并使状态恢复结构更加明显和清晰。

    附录

    OP 问道“那么为什么在这方面静态和动态子级之间存在差异?”好问题。为什么我们有静态子定义、动态监督命令,如supervisor:start_child/2supervisor:delete_child/2 那些奇怪的simple_one_for_one 监督者?

    关键在于您的用例。假设我有一个游戏服务器,需要始终有一个可用的大厅,以便玩家可以登录、聊天、查看军械库网站、用新手问题和反开发者咆哮贴满论坛,并且通常会浪费以其他方式只与实际游戏外围相关的时间。我们从不希望一个崩溃立即将它们全部关闭,但也许我们确实希望能够告诉不同的正在运行的服务在网络上侦听或停止按需接受连接。然而,实际的游戏领域存在于监督树的各自独立分支中——如果其中一个出现故障,我们不希望它带走其他所有东西,我们当然也不希望失去整个集群.

    那么我们将如何构建它?所有的基本服务都将直接写入主管树的子定义中——除非我们手动启动,否则那里没有动态。每当我们启动系统时,它们就会弹出。因为我们可能有任意多个游戏领域,虽然领域定义在内部构造为大部分静态定义的监督树,但每个领域级别的监督者都是管理所有领域的 simple_one_for_one 监督者的子节点(因此,如果该监督者出现故障,那么噗!每个人都回到了大厅,可能很生气)。可以根据我们的命令、设置文件或数据库数据或这些的组合来启动领域。

    不过,延迟外部网络服务的启动可能是件好事。在启动系统时,我们可能有一些重要的启动任务,并且无论如何都可能必须在集群中的不同节点上启动侦听器。作为避免直接通过网络连接给系统带来压力的一种方式,给我们时间检查系统或运行测试,并有机会将系统设置为我们可能希望的某种特定模式(基准测试、测试、锦标赛或其他)延迟外部网络服务的启动。也就是说,我们迫使系统等待我们发送给它的命令,然后它才会为未洗的群众打开狂欢之门。我们会将命令封装在一些可以从 shell 或网络访问的简单调用中(如waste_of_youth:tempt_souls(Node, Port, Cert)),但结果会发生一系列supervisor:start_child/2 调用——而且这些调用是动态的。

    那么如果网络服务管理器死了会发生什么?连接变得POOF!他们不会再次出现,直到我们告诉它们(不仅是活动连接,还有监听器——它们可能已经独立于活动连接而死亡,这取决于主管崩溃的原因),因为系统就是这样设计的。不过,如果这是一个问题,我们可以做很多事情来缓解这种情况。我们可以有一个进程,它的工作是了解和监视某些特定服务的最后状态,例如外部网络——如果它发生意外变化,它会自行启动调用。但这通常不是您想要的——大多数时候,当您需要自动重新启动服务时,您希望在启动时将永久服务的静态定义读入系统。

    上面我提到了我们可能希望在服务器启动时进入的不同模式。将子定义与其余代码分开是很方便的,这样当我告诉它“以游戏测试模式开始”时,它会加载一些不同的主管定义分布(除非我们弄乱它,否则会坚持下去)。如果我告诉它“以生产模式启动”,我们可能会加载包含静态定义的永久网络服务处理程序的子定义。一套适用于各种场合的主管定义——通过这种方式,您可以轻松地创建服务配置文件。这些动态主管命令让您可以在服务状态之间手动切换或将切换过程委托给您在代码中某处定义的命令。

    【讨论】:

    • 那么为什么在这方面静态和动态孩子之间存在差异呢? (请注意,我们不是在谈论子状态的持久性,我只是希望它们与主管一起重新启动(以干净的状态)。)
    • @GaborKulcsar 因为有时您需要主管能够根据外部指令即时执行操作 - 但大多数时间您需要静态定义的子节点(您的核心系统服务、其他监督者、侦听器等),或者一个 simple_one_for_one 监督者,它产生一大批最初相同的子节点(网络连接、客户端资源处理程序、成功或崩溃作业等)。如果我有时间的话,我会用一个例子来说明为什么每个都有用以及在哪里有用。
    • @GaborKulcsar 我添加了一个关于此的讨论。它不是很简洁,希望它不会令人困惑。为了清楚起见,我可能会在这些日子里稍微编辑一下。抱歉,篇幅太长,今晚没时间写一篇短篇。
    • 谢谢 - 你可以添加一个 TL;DR 部分,说 - “不 - 这通常不是你想要的”,这可能会总结这个主题。
    猜你喜欢
    • 2019-04-27
    • 2011-07-26
    • 2014-12-09
    • 1970-01-01
    • 2012-06-22
    • 2021-01-30
    • 2011-06-18
    • 2014-10-25
    • 2023-03-16
    相关资源
    最近更新 更多