このセクションは、 supervisor(3) と密接に関連しています。この関数がスーパバイザの振る舞いの詳細はすべて、この関数で提供されます。
スーパバイザは、子プロセスの起動、停止、モニタリングの責任を持っています。スーパバイザの基本的な考え方は、必要に応じて再起動することで、子プロセスをずっと生きた状態にしておく、というものです。
子プロセスの起動とモニタリングは、 子プロセスの仕様の設定 のリストを渡すことによって設定されます。子プロセスの起動はこのリストで定義された順番で行われ、終了時はこれとは逆の順序で行われます。
gen_serverビヘイビア の章にあった、サーバを起動するスーパバイザのためのコールバックモジュールのコードは以下のようなコードでした:
-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, ...} は 子プロセスの仕様の設定 です。
もし一つ子プロセスが終了したら、他のすべてのこプロセスも終了させられ、最初に終了したものも含めて、すべての子プロセスが再起動します。
one_for_all 監視
子プロセスの一つが終了したら、子プロセスの残り、例えば、起動順序が終了したプロセスの次だった子プロセスなどが終了させられます。その後、子プロセスと、残りの子プロセスが再起動します。
スーパバイザは、指定された時間間隔内で再起動するプロセス数を制限する機能を持っています。これは、スタート時に呼ばれる、 init コールバック関数の返り値に含まれる、MaxRとMaxTという2つのパラメータ値によって決定されます:
init(...) ->
{ok, {{RestartStrategy, MaxR, MaxT},
[ChildSpec, ...]}}.
MaxT秒の間に、MaxRの数値以上に再起動が発生した場合に、スーパバイザはすべての子プロセスを終了させた後に、自分自身を終了させます。
スーパバイザが終了すると、次の上位のスーパーバイザーも同じ行動を起こします。つまり、終了したスーパバイザを再起動するか、自分自身を終了させます。
この再起動メカニズムが提供されている意図は、同じ理由でプロセスが異常終了しているという状況で、全体をもう一度再起動する、という用途で使われることを想定しています。
次のコードは子供の仕様に対するtype定義です:
{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()
StartFunc は子プロセスを起動するのに仕様される関数を定義します。これは「モジュール、関数、引数」のタプルで定義され、 apply(M, F, A) のように使用されます。
ここで設定する関数は、 supervisor:start_link, gen_server:start_link, gen_fsm:start_link, gen_event:start_link 、あるいはこれらを呼び出す関数を設定スべきです。
Restart では、子プロセスが異常終了したときに、再起動すべきかどうかを設定します。
- permanent が設定されると、子プロセスは常に再起動させられます。
- temporary が設定されると、子プロさせうは再起動させられません。
- transient が設定されると、通常とは違う理由で終了したなど、異常終了時にのみ再起動させられます。
Shutdown では、子プロセスを終了すべきときに、どのように行うかを設定します。
- brutal_kill が設定されると、 exit(Child, kill) を使って無条件に終了させられます。
- 整数が与えられると、スーパバイザが exit(Child, kill) を呼び出して強制終了するまでのタイムアウト時間の意味になります。ここで設定された時間だけ、終了シグナルが返ってくるのを待ちます。ここで設定された時間内に終了シグナルが受け取れなかった場合には、 exit(Child, kill) を呼び出して無条件で子プロセスを終了します。
もし、子プロセスが、他のプロセスのスーパバイザである場合には、 infinity を設定して、サブツリーの終了に十分な時間を与えましょう。
Module には、要素が一つのリストを設定します。この要素には、コールバックモジュールの名前を設定します。もし、子プロセスがスーパバイザであれば、 gen_server か、 gen_fsm になります。もし、子プロセスが gen_event であれば、 dynamic を設定します。
この情報は release で説明している、アップグレード、ダウングレードを行うリリースハンドラで使用される情報となります。
サンプル: ch3 サーバ起動する子プロセスの設定は次のようになります。
{ch3,
{ch3, start_link, []},
permanent, brutal_kill, worker, [ch3]}
サンプル: gen_eventビヘイビア の章のイベントマネージャを起動する子プロセスの設定は次のようになります。
{error_man,
{gen_event, start_link, [{local, error_man}]},
permanent, 5000, worker, dynamic}
どちらのサーバも、常にアクセスできる状態であることが期待されているため、 permanent が設定さています。
ch3 は終了時に後処理を行う必要はないため、シャットダウンで何かを行うことはありません。そのため、 brutal_kill で十分です。 error_man はイベントハンドラの片付けにいくらか時間を必要とするため、 Shutdown には500ミリ秒を設定しています。
サンプル: 他のスーパバイザを起動する、子プロセスの定義です。
{sup,
{sup, start_link, []},
transient, infinity, supervisor, [sup]}
上記の例の場合、 ch_sup:start_link() を呼び出すことで、スーパバイザを起動することができます。
start_link() ->
supervisor:start_link(ch_sup, []).
ch_sup:start_link 関数は、 supervisor:start_link/2 関数を内部で呼んでいます。この関数は新しいプロセスをspawnして、スーパバイザとリンクさせます。
このケースでは、スーパバイザは登録されません。その代わり、Pidが直接使われます。名前を指定したい場合には、 supervisor:start_link({local, Name}, Module, Args) もしくは、 supervisor:start_link({global, Name}, Module, Args) を利用します。
新しいスーパバイザプロセスは、コールバック関数の ch_sup:init([]) を呼び出します。返り値としては、 {ok, StartSpec} が期待されます。
init(_Args) ->
{ok, {{one_for_one, 1, 60},
[{ch3, {ch3, start_link, []},
permanent, brutal_kill, worker, [ch3]}]}}.
スーパバイザは、起動仕様にしたがって、指定されたすべての子プロセスを起動します。この場合、一つの子プロセスの ch3 が起動します。
supervisor:start_link は同期実行されます。この関数は、すべての子プロセスが起動し終わるまでは処理は帰ってきません。
静的な監視ツリーに加えて、次のようなコードを使って、既存のスーパバイザに子プロセスを動的に追加することもできます。
supervisor:start_child(Sup, ChildSpec)
Sup には、スーパバイザのpidもしくは名前を設定します。 ChildSpec には 子プロセスの仕様の設定 を渡します。
子プロセスは 、他の子プロセスと同じく、``start_child/2`` を使って追加されます。ただし、次のような例外があります。スーパバイザが死んで、再作成される場合、動的に追加されたすべての子プロセスは失われます。
静的、動的に関わらず、すべての子プロセスは次のようなシャットダウン仕様に従って止めることができます。
supervisor:terminate_child(Sup, Id)
停止した子プロセスの仕様は、次のように呼び出すと削除することができます。
supervisor:delete_child(Sup, Id)
Supは、スーパバイザのpidか名前です。Idは 子プロセスの仕様の設定 の中で指定されているidです。
動的に追加された子プロセスを持つ場合、スーパバイザ自身が再起動すると、静的な子プロセスの削除結果は失われます。
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は引数のリストで、子プロセスの初期化時に渡されるリストに追加されます。スタートのための関数を {M, F, A} と定義したとすると、子プロセスは apply(M, F, A++List) という呼び出し方法で作られます。
上記のサンプルの simple_sup モジュールに子供を追加してみましょう。
supervisor:start_child(Pid, [id1])
これを実行すると、子プロセスは apply(call, start_link, []++[id1]) と呼ばれてスタートします。
call:start_link(id1)
スーパバイザが他の監視ツリーの一部である時は、そのスーパバイザによって自動的に終了します。終了するときは、起動したのと逆の順番で子プロセスを終了していきます。それぞれの停止が終わったら、最後に自分自身を終了させます。
Copyright (c) 1991-2009 Ericsson AB