Erlang入门:构建application练习5(监督树)

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

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。