1. 程式人生 > >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能夠保證子程序總隨父程序的退出而退出。