【问题标题】:Why does starting a Supervisor in a GenServer cause problems with process restart behavior?为什么在 GenServer 中启动 Supervisor 会导致进程重启行为出现问题?
【发布时间】:2021-02-23 18:59:35
【问题描述】:
正如问题的标题所述:
为什么在 GenServer 中启动 Supervisor 会导致进程重启行为出现问题?
我发现了一个讨论 here,内容如下:
具体来说:
-
“监督树提供的保证较少,因为进程可能会退出并且监督者不会终止其子进程。”
-
“如果为主管子级命名,这可能会导致问题,因为当重新启动发生在树的更高位置时,命名的子级可能仍然存在(在其 init/1 中调用 start_link 的进程之上”
-
“您失去了一些高级 OTP 功能,例如代码重新加载,因为流程模块是通过遍历监督树来发现的”
根本原因是什么?这是否普遍成立?
参考文献
- Related code changes
- Github issue
【问题讨论】:
标签:
erlang
elixir
erlang-otp
【解决方案1】:
OTP(监督树)建立在 BEAM 功能之上,例如监控、链接、信号和捕获它们。
主管是gen_servers 自己,其唯一目的是监视/重新启动他们的孩子并以标准方式终止他们/死亡。如果您创建了一个生成主管的gen_server,则意味着您在该级别中有所作为,并且常规主管没有晋级。
让我们考虑一下这个 OTP 场景:
P1 - Parent supervisor
|
G1 - GenServer
|
S1 - Children supervisor
|
C1 - Children worker
监督者在终止自己之前等待其所有子节点退出,如果您有一个 gen_server 作为监督者 (G1) 在其所有子节点 (S1) 终止之前由于某种原因死亡,则父节点可能会重新启动gen_server (G1')。这个将产生 S1',而 S1' 又会产生 C1'。
突然有多个 S1 和 C1 实例同时运行,这很可能是个问题。
关于提到的代码重载问题,这意味着code_changed回调树触发器将在G1停止(因为G1不会将它传播到S1),而不是代码不会被加载。
TL;DR:
主管非常专业gen_servers。如果您将常规 gen_server 放在监督树的中间,而没有提供监督者提供的所有保证,那么您会丢失该子树中的一些 OTP 特征。