elixir - 如果子进程达到 max_restarts,则防止 DynamicSupervisor 关闭

标签 elixir erlang-otp

我有一个 DynamicSupervisorrestart: :transient 开头的 child .默认情况下,如果一个 child 异常退出,它会被supervisor重新启动。

但是,按照设计,如果 child 在 3 次重启后失败,监督者本身将退出。从文档:

https://hexdocs.pm/elixir/Supervisor.html#module-exit-reasons-and-restarts

Notice that supervisor that reached maximum restart intensity will exit 
with :shutdown reason. In this case the supervisor will only be restarted
if its child specification was defined with the :restart option set to :permanent
(the default).

由于杀死主管也会杀死其他 child (正在进行的后台工作),我想避免这种情况。

问题是:到达max_restarts后,如何杀死失败的子进程,保留主管及其其他子进程?

使用 Elixir 1.6/OTP 20。

更新:I found this answer在 StackOverflow 上,本质上建议顶级 DynamicSupervisor 为每个 child 启动一个 DynamicSupervisor;顶层将以 restart: :permanent 启动子主管或 :temporary .这是一个很好的解决方法,但如果有其他解决方案,我会很感兴趣。

最佳答案

DynamicSupervisor遵循与常规 Supervisor 相同的重启策略它的工作方式是有充分理由的。我们需要了解为什么会这样,而不是试图解决这种行为。

了解主管的目的

监督者监视它的 child ,如果意外故障导致其中任何一个宕机,它将以已知的初始状态重新启动它。理解重启限制背后的基本原理的关键在于意外故障的定义。

这里的意外并不意味着您在将未经测试的代码投入生产之前没有考虑过的事情。这种情况仅在正常测试期间难以模拟的罕见情况下发生,难以重现且不经常发生。

即使默认限制为 5 秒内重新启动 3 次,也很难捕获此类故障。事实上,这个限制对于实时系统来说太保守了。我认为它对于在开发早期捕获错误非常有用。当一个错误导致进程立即关闭或在启动后不久关闭时,它很快就会达到 3 次重启并导致其主管死亡。在这一点上,您应该查找错误并修复它。

一种不同的失败方式

假设您确实测试了您的代码并且仍然观察到进程定期死亡,那么您可能会遇到一种不同类型的失败——一种预期的失败。我强烈建议阅读 Fred Hebert 的文章 It's About the Guarantees其中详细介绍了应该使用主管的方式以及他们应该提供的保证。它的一个非常简短和删节的版本:

Supervised processes provide guarantees in their initialization phase, not a best effort. This means that when you're writing a client for a database or service, you shouldn't need a connection to be established as part of the initialization phase unless you're ready to say it will always be available no matter what happens.



如果您确实需要在进程的 init() 中建立到数据库的连接回调,连接失败确实意味着该进程无法运行并且应该死亡。当它被监督者重新启动但它仍然失败时,这确实意味着整个监督树不能正常运行并且应该死亡。这将递归地继续,直到到达根主管并且整个系统出现故障。

现在,Elixir 为诸如此类的各种问题提供了很多开箱即用的解决方案。从某种意义上说,这真的很好,但它也常常使这些问题不可见,让新来者不知道它们的存在。例如,当无法建立到数据库的连接时,Ecto 在后台依赖 db_connection 来提供默认的指数退避。此行为在 db_connection’s docs 中有所描述。 .

那你该怎么办?

回到你的问题,在这一点上应该很清楚,必须为一个经常失败的过程采用另一种方法,而不是导致它的错误。你需要承认它的失败是意料之中的,并在你的代码中明确地处理它。

也许,您的流程依赖于有时可能不可用的外部服务。在这种情况下,您需要使用断路器。有一个用 Erlang 写的叫做 fuse,它的作者在这个 comment on Hacker News 中有很好的描述。 .

Netflix 有一个 blog post展示了在他们的 API 中使用断路器,每天收到数十亿的请求。这是一个令人难以置信的规模,而且现在规模更大,因为那篇文章是 2011 年的!

如果这仍然不是您遇到的那种失败,那么,也许您运行的是无法依赖的不受信任的代码?将它包装在一个 try-rescue 块中并将错误作为值返回,而不是依靠主管为您神奇地处理它们。

我希望这有帮助。

关于elixir - 如果子进程达到 max_restarts,则防止 DynamicSupervisor 关闭,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48774543/

相关文章:

Elixir:将位列表转换为二进制

ubuntu - 无法运行 Phoenix 服务器。找不到 erlang/lib/parsetools-2.1.8

elixir - 一次性密码 : Start GenEvent under Supervisor With Name

erlang - 在 Elixir 中,依赖应用程序是否受到监督?

erlang - 使用未注册、动态创建的 gen_server 的示例?

erlang - 为什么Erlang/OTP gen_server回调模块必须提供handle_cast函数?

java - 通过 Jinterface 调用 gen_server?

websocket - 状态未拾取用户离开事件?

elixir - 如何生成带参数的匿名函数?

elixir - 在生产模式下在 Phoenix 中读取文件