Erlang:RabbitMQ原始碼分析 3. supervisor和supervisor2深入分析
supervisor也是Erlang/OTP裡一個常用的behavior,用於構建supervisor tree實現程序監控,故障恢復。
而RabbitMQ實現了一個supervisor2,我們從原始碼角度分析二者的實現和區別。
先介紹一些supervisor的基本概念,假設node_manager_sup是一個supervisor,它的init函式會定義supervisor的一些引數和它的children。
引數:
1. Restart Strategy:
Strategy必須是simple_one_for_one,one_for_one, one_for_all, rest_for_one 中的一種
simple_one_for_one是指supervisor啟動時並不啟動children,children個數不限,但只能是同一個型別的child,共享一份程式碼
one_for_one是指supervisor啟動時就啟動所有children,一旦一個child掛了,supervisor只去重啟這一個child process,不影響其他children
one_for_all是指supervisor啟動時就啟動所有children,一旦一個child掛了,supervisor重啟所有children processes
one_for_rest是指supervisor啟動時就啟動所有children,一旦一個child掛了,supervisor重啟在這個child之後宣告的所有children
2. intensity and period:如果在period時間內重啟超過intensity 次,supervisor就把所有children連同自己一起kill掉
Children的引數:
1. StartFun,child啟動函式,必須返回{ok,ChildPid} or {ok,ChildPid,Info}
2. Restart, 本child的重啟策略, permanent代表一直重啟,temporary代表絕不重啟,transient代表只有當error退出時才重啟
3. ShutDown, 本child process的shutdown策略,brutal_kill代表立刻強行kill掉, 正整數代表timeout,即傳送一個kill的request,如果timeout時間還沒收到response,就強行kill掉,infinity代表只是傳送一個kill的request過去,不強行kill,一般用於當child也是一個supervisor的時候。
supervisor也是一個gen_server
supervisor2並沒有使用gen_server2而是使用了原版的gen_server
既然是gen_server,supervisor的入口start_link,其實也就是內部的init函式:
1. 檢查supervisor的所有引數
2. 如果不是simple_one_for_one, 就啟動所有children。
當有child process exit 時,根據gen_server的behavior,會由handle_info({'EXIT', Pid, Reason}, State)來處理,根據此child的Restart型別進行重啟。
重啟之前會更新重啟次數,如果發現在period時間內重啟超過intensity 次,就把所有children連同自己一起kill掉
如果重啟失敗會再次重啟,再次重啟的請求由handle_cast處理
最後再看supervisor的幾個export函式:
start_child, 是一個gen_server:call,
1. 如果是simple_one_for_one,就從children裡隨便拿一個啟動,因為simple_one_for_one的children都一樣,而且都沒隨supervisor啟動
2. 如果不是simple_one_for_one,start_child就傳入一個child,啟動這個child
restart_child,delete_child, terminate_child,which_children,which_children也都是gen_server:call
supervisor2:
supervisor2並沒有使用RabbitMQ自己的gen_server2而是使用gen_server,原因supervisor接收的request比較少(都是重啟,啟動,終止之類),也沒有用到hibernate。
supervisor2對supervisor的改動並不算多:
1. intrinsic, child的Restart增加了intrinsic型別,和transient很像,不同的是,transient如果child非正常退出,就將其刪除,supervisor照常執行;但intrinsic如果child非正常退出,supervisor也會退出並刪除其他所有children。
2. Delay, 如上所述,在period時間內重啟超過intensity 次,supervisor就把所有children連同自己一起kill掉。supervisor2裡,child的Restart可以寫{permanent, Delay} | {transient, Delay} | {intrinsic, Delay},這樣在period時間內重啟超過intensity 次後,supervisor並不會kill所有,而是等待Delay時間再嘗試重啟該child。