根监督员
-module(root_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1, shutdown/0]).
start_link() ->
supervisor:start_link({local,?MODULE}, ?MODULE, []).
init(_Args) ->
RestartStrategy = {one_for_one, 10, 60},
ListenerSup = {popd_listener_sup,
{popd_listener_sup, start_link, []},
permanent, infinity, supervisor, [popd_listener_sup]},
Children = [ListenerSup],
{ok, {RestartStrategy, Children}}.
shutdown() ->
exit(whereis(?MODULE), shutdown).
如果子进程也是一个监管器,那么在其规范中应将Shutdown
设置为infinity
,以便给子树充足的关闭时间,并且Type
应该设置为supervisor
,我们已经这样做了。
子监管器
-module(popd_listener_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).
start_link() ->
supervisor:start_link({local,?MODULE}, ?MODULE, []).
init(_Args) ->
RestartStrategy = {one_for_one, 10, 60},
Listener = {ch1, {ch1, start_link, []},
permanent, 2000, worker, [ch1]},
Children = [Listener],
{ok, {RestartStrategy, Children}}.
在这里,我们在一个子规范中将Shutdown
的值设置为2000
。 整数超时值意味着监管进程将通过调用exit(Child,shutdown)
来告知子进程终止,然后等待子进程返回带有shutdown原因的退出信号。
监听器
-module(ch1).
-behaviour(gen_server).
-export([init/1]).
-export([handle_cast/2, terminate/2]).
-export([start_link/0]).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init(_Args) ->
erlang:process_flag(trap_exit, true),
io:format("ch1 has started (~w)~n", [self()]),
{ok, []}.
handle_cast(calc, State) ->
io:format("result 2+2=4~n"),
{noreply, State};
handle_cast(calcbad, State) ->
io:format("result 1/0~n"),
1 / 0,
{noreply, State}.
terminate(_Reason, _State) ->
io:format("ch1: terminating.~n"),
ok.
来自Erlang/OTP文档:
如果gen_server
是监督树的一部分,并且由其监督者有序终止,则如果满足以下条件,函数Module:terminate(Reason, State)
将被调用,其中Reason=shutdown
:
gen_server
已设置为处理退出信号,且
- 在监督器的子规范中定义的关闭策略是整数超时值而不是强制杀死。
这就是为什么我们在Module:init(Args)
中调用了erlang:process_flag(trap_exit, true)
。
示例运行
启动根监督者:
1> root_sup:start_link().
ch1 has started (<0.35.0>)
{ok,<0.33.0>}
根监管进程运行并自动启动其子进程,在我们的情况下是子监管进程。子监管进程依次启动其子进程;在我们的情况下,我们只有一个子进程ch1
。
让我们让ch1
评估正常代码:
2> gen_server:cast(ch1, calc).
result 2+2=4
ok
现在是一些糟糕的代码:
3> gen_server:cast(ch1, calcbad).
result 1/0
ok
ch1: terminating.
=ERROR REPORT==== 31-Jan-2011::01:38:44 ===
** Generic server ch1 terminating
** Last message in was {'$gen_cast',calcbad}
** When Server state == []
** Reason for termination ==
** {badarith,[{ch1,handle_cast,2},
{gen_server,handle_msg,5},
{proc_lib,init_p_do_apply,3}]}
ch1 has started (<0.39.0>)
4> exit(normal).
ch1: terminating.
** exception exit: normal
你可能会注意到子进程ch1
被子监督者popd_listener_sup
重新启动了(注意:ch1已经启动(<0.39.0>)
)。
由于我们的shell和根监督者是双向链接的(在根监督器函数start_link/0
中调用supervisor:start_link
,而不是supervisor:start
),exit(normal)
导致根监督器关闭,但它的子进程有一些时间来清理。