首页 > 代码库 > OTP的supervisor tree如何保证子进程一定随父进程的退出而退出

OTP的supervisor tree如何保证子进程一定随父进程的退出而退出

利用OTP行为包构建的应用之所以可靠,是因为我们按照OTP的设计模式,将所有进程组织成了一棵可靠的supervisor tree。每一个supervisor监控其子进程,并在其子进程出错时按照重启策略进行相应的处理。

但是,你是否考虑过,如果supervisor意外终止,其子进程会怎样?当然,直觉告诉我们连监控进程的没有了,所有的子进程应全部终止。但是,你在代码中是否真正考虑过这种情况?你的gen_server可否写过如下代码?

handle_info({‘EXIT‘, Parent, Reason}, _) %% Parent is the supervisor of the gen_server

事实上,无论你写不写上述代码,你的gen_server、gen_event和gen_fsm都会随其supervisor的结束而结束,但是这是为什么呢?

让我们来看看gen_server的源代码gen_server.erl:

328 loop(Parent, Name, State, Mod, hibernate, Debug) ->
329 proc_lib:hibernate(?MODULE,wake_hib,[Parent, Name, State, Mod, Debug]);
330 loop(Parent, Name, State, Mod, Time, Debug) ->
331 Msg = receive
332       Input ->
333         Input
334   after Time ->
335       timeout
336   end,
337 decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, false).

loop是gen_server的主循环,gen_server在初始化结束后即会陷入loop循环,接收消息并交由decode_msg/8处理。

下面让我们来看看decode_msg/8的源代码:

346 decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Hib) ->
347 case Msg of
348 {system, From, Req} ->
349     sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
350               [Name, State, Mod, Time], Hib);
351 {‘EXIT‘, Parent, Reason} ->
352     terminate(Reason, Name, Msg, Mod, State, Debug);
353 _Msg when Debug =:= [] ->
354     handle_msg(Msg, Parent, Name, State, Mod);
355 _Msg ->
356     Debug1 = sys:handle_debug(Debug, fun print_event/3,
357                   Name, {in, Msg}),
358     handle_msg(Msg, Parent, Name, State, Mod, Debug1)
359 end.

第351行我们可以看到,gen_server收到的所有消息在交由我们写的回调函数处理之前,已经在decode_msg/8中做过预处理,而其对{‘EXIT‘, Parent, Reason}的反应便是以相同Reason退出。

在gen_fsm和gen_event中我们也能找到相同的处理,而supervisor本身是gen_server实现的,所以使用OTP定义的四大行为构建的supervisor tree能够保证子进程总随父进程的退出而退出。