1、前言
在《Erlang入门:构建application练习4(进程link的作用)》一文中,
提到了一个问题:
如果系统进程内部发生了错误而异常退出,将会出现什么情况?
在application中,像bank_center这样的系统进程,如果终止了,整个系统就无法正常运行,为了让bank_center意外终止时能自动重启,这里就要请Erlang的监督树出来帮忙了。
演示项目bank3目录结构:
2、编写监督树回调模块bank_sup.erl
监督树的回调模块只有一个函数init/1
%% @author Rolong<[email protected]> -module(bank_sup). -behaviour(supervisor). -export([start_link/0]). -export([init/1]). %% 定义一个启动本监督树的API start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). init([]) -> %% 启动2个子进程 BankCenterSpec = { center %% 指定本进程(在子进程中唯一)的名称 ,{bank_center, start_link, []} %% 进程启动函数:{M,F,A} ,transient %% 重启策略:permanent | transient | temporary ,5000 %% 关闭方式:brutal_kill | int()>0 | infinity ,worker %% 进程类型:worker 或 supervisor ,[bank_center] %% 回调模块名称:[Module] | dynamic }, BankCenterSpec2 = { center2 ,{bank_center2, start_link, []} ,transient ,5000 ,worker ,[bank_center2] }, %% {ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}} {ok, {{one_for_one, 5, 30}, [BankCenterSpec, BankCenterSpec2]}}.
重启策略:
(1)、permanent:终止后总是会被重启
(2)、transient:意外终止后会被重启
什么事意外?
就是进程退出的Reason不是以下3种之一:
normal | shutdown | {shutdown, Term}
(3)、temporary:终止后不会被重启
关闭方式:
(1)、brutal_kill:野蛮方式,直接exit(Pid,kill)
(2)、大于0的整数:先exit(Child,shutdown),如果超过了指定的毫秒数还没有收到退出信号{‘EXIT‘, PiD, Reason},再exit(Pid,kill)
(3)、infinity:exit(Child,shutdown),然后一直等到退出信号{‘EXIT‘, PiD, Reason}为止
问题:如果某个子进程的重启策略为permanent,当这个子进程发生异常,启动后就立马结束,然后又立马被监督树重启,如此下去,如何是好?
init/1的返回值{ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}中的MaxR, MaxT就是为解决这个问题而生的。
示例中MaxR=5, MaxT=30,表示30秒内,允许总重启次数为5,
如果总重启次数超过5次,整个监督树下的进程都会被终止,最后监督树自身也会终止,整个application也就此停止运行了。
init/1的返回值{ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}中的RestartStrategy是监督树的重启策略,共有如下4种:
(1)、one_for_one:这是最常用的一直策略。当某个子进程终止时,只重启这个子进程
(2)、simple_one_for_one:和one_for_one一样,只是子进程是监督树启动后再动态增加的。如游戏中,假设一个玩家就一个进程,每创建一个玩家进程之后,把这个进程添加到监督树中,这就是simple_one_for_one
(3)、one_for_all:不常用,需了解请见文档
(4)、rest_for_one:不常用,需了解请见文档
3、监督树实验
bank_center/bank_center2中增加以下消息处理:
handle_info(error, Money) -> %% Error Return ... {ok, Money}; handle_info(force_stop, Money) -> {stop, "Force STOP", Money}; handle_info(normal_stop, Money) -> {stop, normal, Money}; handle_info(shutdown, Money) -> {stop, shutdown, Money}; handle_info({shutdown,Term}, Money) -> {stop, {shutdown,Term}, Money};
3.1、测试{RestartStrategy, MaxR, MaxT}={one_for_one, 5, 30}
Eshell V5.10.4 (abort with ^G) 1> appmon:start(). {ok,<0.40.0>} 2> bank_center ! normal_stop. ##[<0.38.0>bank_center:59] terminate: normal normal_stop ##[<0.51.0>bank_center:23] Start bank_center ... 3> bank_center ! normal_stop. ##[<0.51.0>bank_center:59] terminate: normal normal_stop ##[<0.53.0>bank_center:23] Start bank_center ... 4> bank_center ! normal_stop. ##[<0.53.0>bank_center:59] terminate: normal normal_stop ##[<0.55.0>bank_center:23] Start bank_center ... 5> bank_center2 ! normal_stop. ##[<0.39.0>bank_center2:59] terminate: normal normal_stop ##[<0.57.0>bank_center2:23] Start bank_center2 ... 6> bank_center2 ! normal_stop. ##[<0.57.0>bank_center2:59] terminate: normal normal_stop ##[<0.59.0>bank_center2:23] Start bank_center2 ... 7> bank_center2 ! normal_stop. ##[<0.59.0>bank_center2:59] terminate: normal ##[<0.55.0>bank_center:59] terminate: shutdown normal_stop 8> ##[<0.34.0>bank_app:16] Stop bank_app! =INFO REPORT==== 17-Jan-2015::16:19:15 === application: bank exited: shutdown type: temporary
从上面可以看出,当一个被监督的进程终止后,立即被重启了,其他进程不受影响,这就是one_for_one。
在30秒内,给bank_center发送了3次normal_stop消息,
给bank_center2发送了3次normal_stop消息,
第六次发送normal_stop消息之后,application: bank已经shutdown
3.2、测试transient重启策略
bank_sup:init/1中的permanent改为transient
Eshell V5.10.4 (abort with ^G) 1> bank_center ! error. ##[<0.38.0>bank_center:59] terminate: {bad_return_value,{ok,0}} error ##[<0.40.0>bank_center:23] Start bank_center ... 2> =ERROR REPORT==== 17-Jan-2015::17:09:15 === ** Generic server bank_center terminating ** Last message in was error ** When Server state == 0 ** Reason for termination == ** {bad_return_value,{ok,0}} 2> bank_center ! force_stop. ##[<0.40.0>bank_center:59] terminate: "Force STOP" =ERROR REPORT==== 17-Jan-2015::17:09:28 === ** Generic server bank_center terminating ** Last message in was force_stop ** When Server state == 0 ** Reason for termination == ** "Force STOP" force_stop ##[<0.42.0>bank_center:23] Start bank_center ... 3> 3> bank_center ! normal_stop. ##[<0.42.0>bank_center:59] terminate: normal normal_stop 4> 4> whereis(bank_center). undefined 5> supervisor:restart_child(bank_sup, center). ##[<0.46.0>bank_center:23] Start bank_center ... {ok,<0.46.0>} 6> whereis(bank_center). <0.46.0> 7> bank_center ! shutdown. ##[<0.46.0>bank_center:59] terminate: shutdown shutdown 8> whereis(bank_center). undefined 9> supervisor:restart_child(bank_sup, center). ##[<0.51.0>bank_center:23] Start bank_center ... {ok,<0.51.0>} 10> whereis(bank_center). <0.51.0> 11> bank_center ! {shutdown, test}. ##[<0.51.0>bank_center:59] terminate: {shutdown,test} {shutdown,test} 12> whereis(bank_center). undefined
由上可见,进程退出的Reason为normal或shutdown或{shutdown, Term}时,bank_center不会被重启,其他Reason时会被重启。
4、完整演示代码下载
地址:http://download.csdn.net/detail/u011471961/8371361