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

时间: 2024-10-12 11:30:26

Erlang入门:构建application练习5(监督树)的相关文章

[Erl_Question08] Erlang怎么构建一个application?

问题: 当我们把一个项目中所有的supervision tree通过一个简单的函数game: start(),会发现这个树结构特别复杂,只能有一个根节点,然后一直扩展. 这里,这就是application出现的原因,设计一个可以随时开关的子块(application).比如:上图中的log app, db app ,game app, connect app .. 这样对这些应用的开关管理就非常方便啦,[试想你如果用supervisor,运行时还要手动去停进程树,然后还要移除监控树,还要做cle

Erlang入门:构建application练习3(随机启动)

1.构建目录结构 我这示例中,项目文件夹为bank1 ebin目录:存放编译出来的beam文件 include目录:存放hrl文件 src目录:存放erl源文件 2.创建Emakefile文件 编译时,编译器会根据Emakefile文件做相应处理 { [ 'src/*' %% 指定erl源文件目录 ] ,[ debug_info ,{i, "include"} %% 指定include文件夹 ,{outdir, "ebin"} %% 指定编译出来的beam文件存放目

Erlang入门:构建application练习4(进程link的作用)

1.示例项目(bank)简介 bank_server.erl是为客户服务的进程回调模块 bank_center.erl是为银行服务中心的进程回调模块 客户(bank_server)每年都要扣除年费 所有年费都交给银行服务中心(bank_center) 客户服务使用示例: 创建用户:bank_server:create_account(myname, 100). 用户存钱:myname ! {deposit, 10}. 用户取钱:myname ! {cash1, 20}. myname ! {ca

Erlang入门:构建application练习1

1.定义application回调模块 %% 文件名为bank_app.erl -module(bank_app). %% 指定behaviour为application -behaviour(application). %% 导出application回调模块的函数 -export([start/2, stop/1]). %% 启动application时调用 start(_Type, _Args) -> ("Start ~p ...", [?MODULE]), {ok, se

Erlang入门:构建application练习2

1.定义application回调模块 2.定义application资源描述文件 资源描述文件名要和application名称一致,并且后缀为.app, 如:bank.app 然后把资源描述文件放在erlang能搜索到的路径下 3.启动application application:start(bank). 和练习1相比,这里的application启动之前, 不需要自己调用application:load/1加载application 启动application监控器查看applicatio

Erlang入门(二)—并发编程

Erlang中的process——进程是轻量级的,并且进程间无共享.查了很多资料,似乎没人说清楚轻量级进程算是什么概念,继续查找中...闲话不提,进入并发编程的世界.本文算是学习笔记,也可以说是<Concurrent Programming in ERLANG>第五张的简略翻译. 1.进程的创建    进程是一种自包含的.分隔的计算单元,并与其他进程并发运行在系统中,在进程间并没有一个继承体系,当然,应用开发者可以设计这样一个继承体系.    进程的创建使用如下语法: Pid = spawn(

Erlang入门(五)——补遗

暂时搞不到<Programming Erlang>,最近就一直在看Erlang自带的例子和Reference Manual.基础语法方面有一些过去遗漏或者没有注意的,断断续续仅记于此. 1.Erlang的保留字有: after and andalso band begin bnot bor bsl bsr bxor case catch cond div end fun if let not of or orelse query receive rem try when xor 基本都是些用于

webpack入门——构建简易版vue-cli

用vue-cli1/2搭建一个vue项目时,可以看到有很多关于webpack配置的文件.我们不需要知道那些繁琐的配置文件有什么作用,只需在控制台输入npm run dev,项目自动启动,我们就可以愉快的写业务代码了. 虽然vue-cli帮我们做好了一切,我们就能不用学webpack了吗?NoNoNo...现代前端工程师必备的技能就是模块化构建打包项目,不信去那些招聘网站的前端JD看看.废话不多说,下面让我们用webpack构建一个简单的vue-cli. 第一步:安装NodeJS(webpack基

erlang 入门笔记

1.下载安装erlang,我使用的是windows 64 版本的,直接安装,设置环境变量%ERL_HOME%\bin (设置环境变量只是使用方便而已,可设置也可不设置) 2.从cmd 中 启动erlang命令行,如果没有设置环境变量,则需要cd到erlang/bin中 C:\Users\Administrator>erl Eshell V6.4 (abort with ^G) 1> 3.编写helloworld.erl (erlang源文件后缀为.erl) %%erlang中 标识代码结束使用