监控树的重新认识
先介绍一下one_for_one:
eg:
{{one_for_one, 3, 10},
{
mod,
{mod, start_link, []},
transient,
100,
worker,
[mod]
}
}
重启策略(前提:进程被终止)one_for_one,一个进程挂掉只会在10秒内重启3次该进程
首先要说明的是normal是无法终止进程的
temporary:终止后不会被重启,表示子进程决不重启(eg:exception/kill/shutdown 都不重启)
permanent:终止后总是会被重启,表示子进程始终重启(eg:exception/kill/shutdown 都会重启),这个蛮危险的,一直在无脑重启,一旦重启子进程失败了,其实就会导致父进程(supervisor)也连带崩溃
transient:意外终止后会被重启,表示只有在子进程异常终止时才重启(eg:只有shutdown不会重启,exception/kill都会重启)
平时终止进程我们可以使用:
gen_server:call/cast/(Pid, {stop}), %% 这样比较安全可控,回调终止函数 terminate
如果不是gen_server的形式创建的进程,例如loop
一般会使用exit(Pid, exception/shutdown/kill)来终止进程,但是normal是完全没效果的
是否主动捕捉终止进程信息:
如果设置了process_flag(trap_exit, true),则可以在handle_info或者receive 里面接收到'EXIT', Reason(exception/shutdown/kill/normal)
但是如果你的handle_info是这么写:
handle_info(_Info, State) ->
io:format("stllllllllllllllllllll ~p~n", [_Info]),
{noreply, State}.
那么只会捕捉到退出信息{'EXIT', Pid, Reason} = _Info,但是不会终结进程,但是kill除外!!!,exit(Pid, exception/shutdown) 都可以捕捉到
但是exit(Pid, kill) 是无法捕捉的,如果有重启策略,那么该进程被kill了后,会直接重启,不会调用terminate
如果的handle_info是这么写:
do_info({'EXIT', Pid, _Reason}, State) ->
case _Reason of
normal ->
{noreply, State};
_ ->
{stop, mod_user_error_exit, State}
end;
表示捕捉到了退出信息'EXIT',那么你完全可以根据自己的意愿去选择是否要终止该进程,然后回调terminate,注意kill是不会走到这步的!!!同时也要注意
在这里normal也是可以选择是否终止该进程。
如果是process_flag(trap_exit, false),即默认不添加捕捉,虽然也会根据重启策略,但是exit(Pid, Reason)终止进程是不会回调terminate,直接重启,
除非你不是通过exit,而是通过stop 终止进程,无论如何terminate都必须在,stop的情况下才能回调。
还要注意的是:
one_for_one 重启失败后(假如某个子进程在init的时候一直报错),会导致他的父进程崩溃,一直崩溃到第一个supervisor监控树进程
例如 server_sup(one_for_one,transient) -> listen_sup(one_for_one,transient) -> mod_listen(gen_server) -> mod_rand(gen_server)
假如mod_rand崩溃了,并且重启失败init一直报错,那么mod_rand最终会影响到listen_sup,导致listen_sup也挂掉,但是不会影响到server_sup
假如listen_sup挂掉(只有exit(Pid, kill)才能杀死supervisor),无法正常重启,那么server_sup也会崩溃,导致全服崩了!!!!
simple_one_for_one就是单纯的,为了实现动态加载模块
例如:
server_tcp_client_sup
{ok, {{simple_one_for_one, 10, 10},
[{server_reader, {server_reader,start_link,[]},
temporary, brutal_kill, worker, [server_reader]}]}}.
这是并不会立即创建server_reader进程,因为需要在其他进程使用supervisor:start_child动态创建
{ok, Child} = supervisor:start_child(server_tcp_client_sup, []), %% 调用supervisor:start_child(simple_sup, Args)后,最终启动子进程的代码会是:apply(M, F, A++Args).
并且创建的server_reader进程都是只有一种模式,不能像one_for_one那样配置多种方式