【整理】Erlang 中的 supervisor

【概况】

supervisor behaviour 是用来实现监控其他子进程的 supervisor 进程的模块;  
子进程可以是另一个 supervisor 进程,也可以是一个 worker 进程;  
worker 进程一般使用 gen_event,gen_fsm 或 gen_server behaviour 来实现    
一个使用 supervisor behaviour 实现的 supervisor 有一个接口方法的标准集,包括跟踪和错误报告的功能;  
supervisor 机制用来构建一个分层进程结构,称为 supervision tree,这是组织一个容错系统的好方式。  

【原则】

supervisor 负责启动、停止和监控它的子进程;  
supervisor 在必要时,通过重启它的子进程来使其保持活着;  
supervisor 的子进程由称作子进程规范的列表进行定义;  
当 supervisor 启动时,子进程按子进程规范列表中内容从左至右的顺序启动;  
当 supervisor 终止时,会按子进程启动顺序的反顺序终止相应子进程。  

【重启策略】

one_for_one  
如果一个子进程停止,则只重启该子进程;  
one_for_all  
如果一个子进程停止,则所有其他子进程也停止,然后所有子进程重启;  
rest_for_one  
如果一个子进程停止,则启动顺序中在它之后的所有其他子进程也停止,然后停止的这些子进程重启;  
simple_one_for_one   
一个简化的 one_for_one ,所有的子进程都是同样进程类型并且是动态添加的实例;  

【最大重启频率】

supervisor 有一个自带的机制来限制给定时间内重启的次数;  
如果在最近的 MaxT 秒之内有超过 MaxR 次数的重启,则 supervisor 停止它本身和它所有的子进程;  
当 supervisor 停止后,下一个更高级别的 supervisor 将进行下一步动作,重启上述停止的 supervisor 或者终止本身;  
重启机制的意图是防止一个进程由于某些原因重复性的死掉;  

【子规范】

?
1
{Id, StartFunc, Restart, Shutdown, Type, Modules}
Id   用于 supervisor 内部识别子规范的名字;  

StartFunc   定义了用来启动子进程的的方法,由三元组 {M, F, A} 来确定,其一般情况下调用的是  supervisor:start_link     gen_server:start_link   gen_fsm:start_link   或  gen_event:start_link   ,或对上述函数的相应封装。   

Restart   定义了子进程什么时候重启:  
  • permanent 表示子进程始终重启;
  • temporary 表示子进程决不重启;
  • transient 表示只有在子进程异常终止时才重启,即除了 normal 以外的终止原因;

Shutdown   定义了子进程怎样终止:  
  • brutal_kill 表示子进程使用 exit(Child, kill) 来无条件的终止;
  • 一个整数 timeout 值表示 supervisor 通过调用 exit(Child, shutdown) 告诉子进程请终止自己,然后等待子进程返回退出信号;如果没有在指定的时间内接收到(来自子进程的) 退出信号,则使用 exit(Child, kill) 无条件终止子进程
  • 如果子进程是另一个 supervisor,则应该设置为 infinity 来给子树足够的时间来终止;

Type   指定子进程是一个 supervisor 还是一个 worker;  

Modules   应该是一个 list,含有一个元素 [Module]。  
如果子进程是一个 supervisor,或者子进程是 woker 且采用 gen_server 或 gen_fsm 行为模式实现,则 Modules 是 callback 模块的名字;  
如果子进程是 worker 且采用 gen_event 行为模式实现,则 Modules 应该为  dynamic   ,该信息用来在升级和降级时供 release handler 使用;  

【参数选择原则】

按照子进程规范启动的子进程,若是要求该子进程为任何时候均可访问的注册进程,则一般使用参数 permanent ;  
按照子进程规范启动的子进程,若是要求该子进程终止之前不需要做任何清理工作,则不需要设置 timeout 值,可以设置为 brutal_kill  
若是要求其终止之前做相应资源清理,则需要设置 timeout 超时值。   

【启动 supervisor】

?
1
supervisor:start_link(CallbackModuleName, InitCallbackParamList).
启动一个新的 supervisor 进程并链接到 OTP 系统的监督树中;  
  • 第一个参数 callback 模块的名字,是 init callback 方法所在 module 的名字;
  • 第二个参数 init callback 方法的参数列表;
supervisor 的执行流程如下:  
supervisor:start_link  --> init --> 根据指定的子规范的入口来启动它的所有子进程  

注意   supervisor:start_link 是同步的,当所有子进程启动之后才会返回    

【动态添加子进程】

?
1
supervisor:start_child(Sup, ChildSpec)
Sup 是 supervisor 的 pid 或名字,ChildSpec 是子进程规范;  
使用 supervisor:start_child/2 添加的子进程会表现出像其他子进程一样的行为,除了这点,如果 supervisor 死掉然后重启,则所有动态添加的子进程都将丢失  

【停止一个子进程】

任何子进程,不管静态的还是动态的,都可以使用 shutdown 规范来停止。  
?
1
supervisor:terminate_child(Sup, Id)
停止的子进程的子规范使用如下调用来删除。  
?
1
supervisor:delete_child(Sup, Id)
Sup 是 supervisor 的 pid 或 name,Id 是子规范里指定的 id 。  
就像动态添加的子进程一样,如果 supervisor 本身重启,那么删除静态子进程的效果会丢失  

【simple_one_for_one】

采用 simple_one_for_one 重启策略的 supervisor 是一个简化的 one_for_one supervisor,所有的子进程都是动态添加的同一类型子进程的实例。  
?
1
2
3
4
5
6
7
8
9
10
11
12
13
-module(simple_sup).
-behaviour(supervisor).
 
- export ([start_link /0 ]).
- export ([init /1 ]).
 
start_link() ->
   supervisor:start_link(simple_sup, []).
 
init(_Args) ->
   {ok, {{simple_one_for_one, 0, 1},
     [{call, {call, start_link, []},
       temporary, brutal_kill, worker, [call]}]}}.
当 supervisor 通过 supervisor:start_link/2 启动后,将不会启动任何子进程,而是通过调用如下代码来动态添加子进程:  
?
1
supervisor:start_child(Sup, List)
Sup 是 supervisor 的 pid 或 name,List 是一个任意的 term 列表,该列表值将会被动态添加到子规范的参数列表里;  
如果启动方法指定为 {M, F, A},则子进程的启动是通过调用 apply(M, F, A++List) 来完成的。   

【supervisor 的终止】

既然 supervisor 是 supervision tree 的一部分,则其将自动被它自身的 supervisor 所终止;当终止时,它会按启动的反顺序根据相应的 shutdown 规范来自动终止它所有的子进程,然后终止本身。  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值