1. 程式人生 > >Erlang supervisor重啟策略

Erlang supervisor重啟策略

最近閱讀了一下erlang的supervisor模組,自己寫了一些簡單的程式碼來區分不同重啟策略的區別,這裡記錄一下。

首先為了能夠直觀的觀察,我把程式碼寫成了一個application方便用工具觀察。

接著實現一個supervisor模組,部分程式碼如下:

start_link(_Args) ->
    io:format("example_sup start_link ~n"),
    {ok, Sup} = supervisor:start_link({local, ?MODULE}, ?MODULE, []),
    {ok, Sup}.

init(_Args) ->
    io:format("example_sup init~n"),
    {ok, 
{{one_for_one, 3, 10}, [{temporary1, {example_child, start, [temporary1]}, temporary, 1000, worker, [example_child]}, {temporary2, {example_child, start, [temporary2]}, temporary
, 1000, worker, [example_child]}, {transient1, {example_child, start, [transient1]}, transient, 1000, worker, [example_child]}, {transient2, {example
_child, start, [transient2]}, transient, 1000, worker, [example_child]}, {permanent1, {example_child, start, [permanent1]}, permanent, 1000, worker, [example_child]}, {permanent2, {example_child, start, [permanent2]}, permanent, 1000, worker, [example_child]} ] }}
.

init函式中按照格式定義了六種子程序規範,但是呼叫的是同一個回撥模組,我只是為了理解重啟方式,沒必要每個規範都單獨定義一個回撥模組。

之所以寫了六個子程序規範是因為三種重啟方式,正常和異常終止兩種情況一共需要六個程序一次就可以測試對比出來一種重啟策略。

六個程序以啟動方式分為三組:temporary,permanent,transient,每組中的兩個程序分別分別以後綴1,2來表示,也就是temporary1, temporary2這樣,對於字尾為1的程序進行正常終止操作,對於字尾為2的程序進行異常終止操作,操作方法呼叫example_child:kill(RegName, Reason),RegName就是上面說的temporary1,2等這些程序註冊名,Reason正常終止為normal,異常的隨便

來看one_for_one重啟策略:

我們呼叫example_app:start()啟動該測試應用,我在每個模組的啟動函式中都加了列印,子程序呼叫的同一個模組,所以我把子程序規範中的子程序識別符號作為引數傳遞進入啟動函式中,這樣就知道哪個子程序啟動了。

1> example_app:start().
    example_sup start_link
    example_sup init
    temporary1 init
    temporary2 init
    transient1 init
    transient2 init
    permanent1 init
    permanent2 init
    app start application
    ok

正如文件中所說,子程序的啟動順序是列表中從左到右,可以看到列印是先啟動了example_sup監督程序,然後回撥example_sup:init函式,之後按照從左到右的順序依次啟動了六個程序

我們開啟observer工具,如下圖,圖中程序監控樹中程序的順序不代表它的啟動順序:

這裡寫圖片描述

接下來,我們分別對三組程序中的兩個程序進行正常和異常兩種終止方式,然後看結果對比

  • 首先對於temporary1進行正常終止:
    example_child:kill(temporary1, normal).
    kill temporary1/<0.129.0>
    [temporary1] handle_cast {exit, normal}
    ok
    [temporary1] terminate normal

    可見temporary啟動方式正常終止不會重啟,因為子程序啟動時會列印init語句

  • 再對temporary2進行異常終止:
    example_child:kill(temporary1, normal).
    kill temporary1/<0.129.0>
    [temporary1] handle_cast {exit, normal}
    ok
    [temporary1] terminate normal

    20180423 修改
    這裡貼的結果有誤,複製錯了,後面有空了在修改

    還有一堆錯誤報告就不列出來了,發現temporary啟動方式異常終止也不會重啟

  • 再來看permanent啟動方式:
    example_child:kill(permanent1, normal).
    kill permanent1/<0.133.0>
    [permanent1] handle_cast {exit, normal}
    ok
    [permanent1] terminate normal
    permanent1 init
    example_child:kill(permanent2, unnormal).
    kill permanent2/<0.134.0>
    ok
    [permanent2] handle_cast {exit, unnormal}
    [permanent2] terminate unnormal
    permanent2 init

    正如文件所說,permanent啟動方式,子程序總是被重啟

  • 再看transient方式:
    example_child:kill(transient1, normal).
    kill transient1/<0.131.0>
    ok
    [transient1] handle_cast {exit, normal}
    [transient1] terminate normal
    example_child:kill(transient2, unnormal).
    kill transient2/<0.132.0>
    ok
    [transient2] handle_cast {exit, unnormal}
    [transient2] terminate unnormal
    transient2 init

    可以看出transient啟動方式子程序只有在非正常情況下終止才會被重啟,正常終止時是不會重啟的

    以上所說都是在one_for_one重啟策略下的,在one_for_all和rest_one_for_one中可以按照上述方法測試,就可以看出哪些程序會被重啟

    至於simple_one_for_one和one_for_one的區別就是子程序規範只能有一個,子程序是動態新增,我們來修改下example_sup的程式碼,直接將one_for_one改為simple_one_for_one,直接重啟這個application會發現會報錯,因為simple_one_for_one策略子程序規範只能有一個,我們試試將子程序規範設定為空列表,啟動發現也會報錯,我們把其他的都註釋掉,只留一個子程序規範啟動發現成功了:

    example_app:start().
    example_sup start_link
    example_sup init
    app start application
    ok

    我們發現沒有子程序,因為子程序需要動態新增,原始碼在附件中
    文中程式碼附件