为什么要有一个监督树而不是只有一个集中的监督者?

Why have a supervision tree instead just one centralised supervisor?

我正在学习 Elixir 和 Erlang/OTP,想了解在构建高可用性系统中拥有监督树的重要性。

我可以看到主管在管理工作进程的生命周期中的重要性。但是,我还是想知道为什么有些应用程序需要以层级的形式组织supervisor,而不是让一个supervisor来管理所有的worker?我天真地忽略了这样的结构有什么实际好处吗?

借用Programming Elixir书中的例子,在哪种情况下我们更喜欢第一种结构而不是第二种结构?

1.  MainSupervisor
    ├── StashWorker
    └── SubSupervisor
        └──SequenceWorker

2.  MainSupervisor
    ├── StashWorker
    └── SequenceWorker

你可能忽略了著名的“让它崩溃”的哲学,它使进程崩溃并重新启动 OTP 中的 first-class 公民。我们不会将进程崩溃视为失败,而是将其视为无需手动处理错误即可正确重做的机会。

主要原因是允许对失败时应该重新启动的内容进行更精细的控制。为此,我们有 strategies。或者,正如@Andree 在评论中重申的那样:

by organizing supervisions in hierarchies, we allow finer-grained control over how the system should respond should a subset of the system fails

想象一下应用程序有一个负责远程连接的进程,还有一堆进程,都在使用这个资源。当连接进程崩溃时,在任何情况下,它都会被其主管重新启动,但它的 pid 发生了变化。这意味着所有依赖此 pid 的进程也应该重新启动。使用 :rest_for_one 策略,开箱即用。

此特定示例的另一种方法是管理进程中的连接,在树的另一部分进行监督,并在出现连接问题时使用此连接手动使池的主管崩溃 重新初始化所有这些。

甚至更多,我们可能希望手动使处理此连接的进程崩溃以重新初始化它,而不是像 if no_conn, do: reload_config_and_restart_connection 那样编写防御性代码,我们只是让它崩溃并由监督树使用新的适当配置重新初始化.

最后但并非最不重要的一点是,如果主管不捕获退出,它也会崩溃,并向上传播。这样我们就可以在不写一行代码的情况下重新初始化监督树的整个分支。