万事皆有因

这件事情的开始是非常加单的,我想把几个模块独立化,变的可以重复使用。然后就创建了几个application,但是将几个模块整合到同一个项目的时候,犯了一个小小的错误,这个错误虽然不是很致命,但是是一个非常不好的。


产生原因

一开始,我有一个主干项目A,创建了SupervisorA。之后我创建了一个项目B,同时创建了一个SupervisorB。然后将两个项目整合的时候,我项目B中的模块X的start_link的代码如下面这样

-module(mod_x).
-export([start_link/1]).
start_link(opts)->
 Child = {mod_y,{mod_y,start_link,[]},permanent,5000,worker,[mod_y],
 supervisor:start_child(supervisor_b,Child).

然后我在主干项目中的SupervisorA的代码写成了这个样子

-module(supervisor_a).
-export([start_link/0]).
-export([init/1]).
start_link() ->
  supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([])->
  RestartStrategy = {one_for_one, 5, 10},
  MFA = {mod_x,start_link,[]},
  Child = {mod_x,MFA,permanent,5000,worker,[mod_x]},
  {ok, { RestartStrategy,[Child]} }.

此时此刻我想大家已经看出问题了。


解释下

这样执行下来,当打开observer会观察到mod_y的进程是和SupervisorB关联,而不是和SupervisorA关联。我先说下为什么是和SupervisorB关联而不是和SupervisorA关联。然后再说下会有什么问题。

为什么和SupervisorB关联。

因为supervisor的start_child方法,是通过gen_server:call的方式,让指定的Supervisor通过start_link来创建child。而supervisor的start_link也是让指定的Supervisor通过mod:start_link来创建。换句话,就是supervisor并不是使用monitor机制,而是使用trap_exit机制来监控它所创建的进程。

那么会出现什么问题。

如果mod_y这个进程,不小心死掉了,那么当我们通过supervisor:which_children(SupervisorA)去寻找child的pid的时候,我们是无法拿到正确的Pid。原因就是上面那个,mod_y死掉后是SupervisorB负责重启的,而SupervisorA根本不知道这个变化。

05-18 02:53