1. 監督規則
一個監督者負責啓動、停止、監控他的子進程。監督者的一個基本概念就是當必要的時候重啓子進程保證它們的存活
哪個子進程要重啓和被監控是由一個子規程列表決定的,子進程按照列表中指定的順序啓動,並按相反的順序終止
2. 實例
監督者的回調模塊
-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定義了最大重啓頻率
{ch3, …}是子規程
3. 重啓策略
one_for_one
one_for_all
假如一個進程停止了,所有其他子進程也要被停止,然後所有子進程,包括這個引發停止的子進程都被重啓
rest_for_one
假如一個進程停止了,它後面的子進程,也就是以啓動順序來說這個被終止的進程後面的子進程都將被停止,然後他們又被啓動。
4. 最大啓動頻率
監督者有一個內建機制限制在給定的時間間隔裏的重啓次數,這由子進程啓動規程中的兩個參數值決定,MaxR和MaxT,它們定義在回調函數init中
init(...) ->
{ok, {{RestartStrategy, MaxR, MaxT},
[ChildSpec, ...]}}.
如果在時間MaxT裏重啓次數大於MaxR ,監督者進程就停止它所有子進程,然後再終止自己。
當監督者進程終止了,那麼更高級別的監督者要採取些動作,它或者重啓被終止的監督者或者停止自己
這個重啓機制的目的是預防一個進程因某種原因頻繁的終止,然後簡單的重啓。
5. 子規範
下面的是類型定義
{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用來內部標識子規範
StartFunc是啓動子進程時調用的函數,它將成爲對supervisor:start_link, gen_server:start_link, gen_fsm:start_link or gen_event:start_link的調用
Restart標識一個進程終止後將怎樣重啓,一個permanent 進程總會被重啓;一個temporary 進程從不會被重啓;一個transient 進程僅僅當是不正常的被終止後才重啓,例如非normal得退出原因
Shutdown 定義一個進程將怎樣被終止,brutal_kill意味着子進程被exit(Child, kill)無條件的終止;一個整數值的超時時間意味着監督者告訴子進程通過調用exit(Child, shutdown)而被終止,然後等待一個返回的退出信號,假如在指定的時間裏沒有收到退出信號,那麼子進程用exit(Child, kill)被無條件終止。
Type指定子進程是supervisor還是worker
Modules 是有一個元素的列表[Module],假如子進程是supervisor、gen_server 或 gen_fsm,那麼Module 是回調模塊的名稱;假如子進程是gen_event,那麼Modules 應該是dynamic
例如:子規範用於啓動一個服務器ch3
{ch3, {ch3, start_link, []}, permanent, brutal_kill, worker, [ch3]}
子規範用於啓動一個事件管理器
{error_man, {gen_event, start_link, [{local, error_man}]}, permanent, 5000, worker, dynamic}
監督者然後根據子規程啓動所有子進程,這個例子中是一個子進程ch3
6. 啓動supervisor
像這樣
start_link() ->
supervisor:start_link(ch_sup, []).
啓動
監督者進程調用init
init(_Args) ->
{ok, {{one_for_one, 1, 60},
[{ch3, {ch3, start_link, []},
permanent, brutal_kill, worker, [ch3]}]}}.
並期待init返回{ok, StartSpec}
注意supervisor:start_link是同步的,它一直等到所有子進程都啓動了才返回
7. 添加子進程
除靜態監控樹外,我們也可以通過supervisor:start_child(Sup, ChildSpec)向監督者動態添加子進程,Sup 是監督者的pid或名稱,ChildSpec 是一個子規範。子進程用start_child/2來添加。注意:假如監督者死掉後重啓,那麼所有動態添加的子進程都不復存在
8. 停止子進程
任何靜態動態添加的子進程都可以用supervisor:terminate_child(Sup, Id)來停止。一個停止子進程規範可以用supervisor:delete_child(Sup, Id)來刪除。Sup是監督者的pid或名稱,Id是子規範的id
9. Simple-One-For-One
監督者的simple_one_for_one啓動策略是one_for_one的簡版,所有子進程都是同一進程實例而被動態添加,下面是一個simple_one_for_one監督者的實例
-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:start_child(Sup, List)來動態添加,Sup 是監督者的pid或名稱,List 是添加給子規範中指定參數列表term列表,如果啓動函數是{M, F, A}這種形式,那麼子進程通過調用apply(M, F, A++List)而被啓動
例如,給上面的例子添加一個子進程
supervisor:start_child(Pid, [id1])
那麼子進程通過調用apply(call, start_link, []++[id1])而被啓動,實際上就是call:start_link(id1)
10. 停止
因爲監控者是監控樹的一部分,它自動被他的監督者停止,根據相應規範,它反序停止它的所有子進程,然後終止自己
至此,四種behavour已經全部翻譯完了,熟練應用他們是你構建高擴展、高容錯、高併發應用的基礎,努力吧!
本文屬轉載
原作者:老紀
原載:老紀博客版權所有。轉載時必須以鏈接形式註明轉載自老紀博客 [http://www.jifuyi.com/]。