OTP的supervisor tree如何保證子程序一定隨父程序的退出而退出
阿新 • • 發佈:2019-01-25
利用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能夠保證子程序總隨父程序的退出而退出。