erlang supervisor說明

Supervisor Behaviour是一個用來實現一個supervisor進程來監控其餘子進程的模塊
子進程能夠是另外一個supervisor,也能夠是一個worker進程.
worker進程通常使用gen_event,gen_fsm或gen_server behaviour來實現.
一個使用該模塊來實現的supervisor有一個接口方法的標準集,包括跟蹤和錯誤報告的功能.
supervisor用來構建一個分層進程結構,稱爲supervision tree,這是組織一個容錯系統的好方式

1,Supervision原則
supervisor負責啓動、中止和監控它的子進程
supervisor在必要時經過重啓它的子進程來保持它們活着
supervisor的子被定義爲一個子規範的list
當supervisor啓動時,子進程按list從左至右的順序啓動
當supervisor終止時,它首先按啓動順序的反順序終止它的子進程

2,例子
啓動服務器的supervisor的callback模塊:
-module(ch_sup).
-behaviour(supervisor).
 
-export([start_link/0]).
-export([init/1]).
 
start_link() ->
  supervisor:start_link(ch_sup, []).
 
init(_Args) ->
  {ok, {{one_for_one, 1, 60},
    [{ch3, {ch3, start_link, []},
      permanent, brutal_kill, worker, [ch3]}]}}.

one_for_one是重啓策略之一
1和60定義了最大重啓頻率
tuple {ch3, …}是子規範

3,重啓策略
  3.1 one_for_one
      若是一個子進程中止,則只重啓該進程
  3.2 one_for_all
      若是一個子進程中止,全部其餘子進程也中止,而後全部進程重啓
  3.3 rest_for_one
      若是一個子進程中止,則啓動順序中在它以後的全部其餘子進程也中止,而後中止的這些進程重啓(跟樓上那位不同)
  3.4 simple_one_for_one
      一個簡化的one_for_one supervisor,全部的子進程都是一樣進程類型而且是動態添加的實例

4,最大重啓頻率
supervisor有一個自帶的機制來限制給定時間內重啓的次數
這是經過MaxR和MaxT這兩個參數來決定的
init(...) ->
  {ok, {{RestartStrategy, MaxR, MaxT},
    [ChildSpec, ...]}}.
若是在最近的MaxT秒以內有超過MaxR次數的重啓,則supervisor中止它自己和它全部的子進程
當supervisor中止後,下一個更高級別的supervisor進行下一步動做,重啓該中止的supervisor或者終止自己
重啓機制的意圖是防止一個進程因爲某些緣由重複性的死掉

5,子規範
這是子規範的類型定義:
Module = atom()
{Id, StartFunc, Restart, Shutdown, Type, Modules}
  Id = term()
  StartFunc = {M, F, A}
    M = F = atom()
    A = [term()]
  Restart = permanent | transient | temporary
  Shutdown = brutal_kill | integer() >=0 | infinity
  Type = worker | supervisor
  Modules = [Module] | dynamic
    Module = atom()
Id是用來讓supervisor內部識別子規範的名字

StartFunc定義了用來啓動子進程的的方法,符合module-function-arguments tuple{M, F, A}
它應該調用supervisor:start_link,gen_server:start_link,gen_fsm:start_link或gen_event:start_link,或相適應的方法

Restart定義了子進程何時重啓
1)permanent表示子進程始終重啓
2)temporary表示子進程決不重啓
3)transient表示只有在子進程異常終止時才重啓,即除了normal之外的終止緣由

Shutdown定義了子進程怎樣終止
1)brutal_kill表示子進程使用exit(Child, kill)來無條件的終止
2)一個整數timeout值表示supervisor告訴子進程經過調用exit(Child, shutdown)來終止,而後等待一個exit信號返回
若是沒有在指定的時間內接收到exit信號,則子進程使用exit(Child, kill)來無條件的終止
3)若是子進程是另外一個supervisor,它應該設置爲infinity來給子樹足夠的時間來終止

Type指定子進程是一個supervisor仍是一個worker

Modules應該是一個list,含有一個元素[Module]
若是子進程是一個supervisor,gen_server或gen_fsm則Module是callback模塊的名字
若是子進程是一個gen_event,則Modules應該爲dynamic

該信息用來在升級和降級時供release handler使用

例子:啓動服務器ch3的子規範
{ch3,
  {ch3, start_link, []},
  permanent, brutal_kill, worker, [ch3]}
例子:啓動event manager的子規範
{error_man,
  {gen_event, start_link, [{local, error_man}]},
  permanent, 5000, worker, dynamic}

服務器和event manager都是註冊進程,能夠在任什麼時候候訪問,這樣它們都指定爲permanent
ch3不須要在終止以前作任何清理工做,這樣就不須要timeout,可是必須知足brutal_kill,error_man可能須要一些時間來讓event handler清理,這樣Shutdown設置爲5000ms

例子:啓動另外一個supervisor的子規範
{sup,
  {sup, start_link, []},
  transient, infinity, supervisor, [sup]}

6,啓動一個supervisor
上面的例子經過調用ch_sup:start_link()來啓動supervisor:
start_link() ->
  supervisor:start_link(ch_sup, []).
ch_sup:start_link調用方法supervisor:start_link/2,這個方法啓動一個新的supervisor進程並鏈接它
1)第一個參數ch_sup是callback模塊的名字,它是init callback方法所在的位置
2)第二個參數[]是傳給init callback方法的參數
一個supervisor進程調用callback方法ch_sup:init([]),返回{ok, StateSpec}:
init(_Args) ->
  {ok, {{one_for_one, 1, 60},
    [{ch3, {ch3, start_link, []},
      permanent, brutal_kill, worker, [ch3]}]}}.
而後根據指定的子規範的入口來啓動它的全部子進程,在這裏有一個子進程ch3
注意supervisor:start_link是同步帶,看成有子進程啓動以後纔會返回

7,添加一個子進程
除了靜態的supervision tree,咱們也能夠添加動態子進程到已有的supervisor裏:
supervisor:start_child(Sup, ChildSpec)
Sup是supervisor的pid或名字,ChildSpec是子規範
使用start_child/2來添加的子進程表現出像其餘子進程同樣的行爲,除了這點:若是supervisor死掉而後重啓,則全部動態添加的子進程都將丟失

8,中止一個子進程
任何子進程,無論靜態的仍是動態的,均可以使用shutdown規範來中止:
supervisor:terminate_child(Sup, Id)
中止的子進程的子規範使用以下調用來刪除:
supervisor:delete_child(Sup, Id)
Sup是supervisor的pid或name,Id是子規範裏指定的id
就像動態添加的子進程同樣,若是supervisor自己重啓,那麼刪除靜態子進程的效果會丟失

9,simple_one_for_one supervisor
simple_one_for_one重啓策略的supervisor是一個簡化的one_for_one supervisor,全部的子進程都是動態添加的同一進程的實例
一個simple_one_for_one supervisor callback模塊的例子:
-module(simple_sup).
-behaviour(supervisor).
 
-export([start_link/0]).
-export([init/1]).
 
start_link() ->
  supervisor:start_link(simple_sup, []).
 
init(_Args) ->
  {ok, {{simple_one_for_one, 0, 1},
    [{call, {call, start_link, []},
      temporary, brutal_kill, worker, [call]}]}}.
-module(simple_sup).
-behaviour(supervisor).
 
-export([start_link/0]).
-export([init/1]).
 
start_link() ->
  supervisor:start_link(simple_sup, []).
 
init(_Args) ->
  {ok, {{simple_one_for_one, 0, 1},
    [{call, {call, start_link, []},
      temporary, brutal_kill, worker, [call]}]}}.
當啓動後,supervisor將不會啓動任何子進程,而是經過調用以下代碼來動態添加全部的子進程:
supervisor:start_child(Sup, List)
Sup是supervisor的pid或name,List是一個任意的term列表,將會被動態添加到子規範的參數列表裏
若是啓動方法指定爲{M, F, A},則子進程經過調用apply(M, F, A++List)來啓動
例如,添加一個子進程到simple_sup:
supervisor:start_child(Pid, [id1])
這將會經過調用apply(call, start_link, []++[id1])即call:start_link(id1)來啓動子進程

10,終止
既然supervisor是supervision tree的一部分,則它將自動被它的supervisor終止
當終止時,它會按啓動的反順序根據相應的shudown規範來自動終止它全部的子進程,而後終止自己
補充:supervisor exports and callbacks
supervisor module                  Callback module
supervisor:start_link              Module:init/1
supervisor:start_child
supervisor:terminate_child
supervisor:delete_child
supervisor:restart_child
supervisor:which_children
supervisor:check_childspecs 
相關文章
相關標籤/搜索