【概况】
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}
|
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).
|
- 第一个参数 callback 模块的名字,是 init callback 方法所在 module 的名字;
- 第二个参数 init callback 方法的参数列表;
supervisor:start_link --> init --> 根据指定的子规范的入口来启动它的所有子进程
注意: supervisor:start_link 是同步的,当所有子进程启动之后才会返回 。
【动态添加子进程】
1
|
supervisor:start_child(Sup, ChildSpec)
|
使用 supervisor:start_child/2 添加的子进程会表现出像其他子进程一样的行为,除了这点,如果 supervisor 死掉然后重启,则所有动态添加的子进程都将丢失;
【停止一个子进程】
任何子进程,不管静态的还是动态的,都可以使用 shutdown 规范来停止。
1
|
supervisor:terminate_child(Sup, Id)
|
1
|
supervisor:delete_child(Sup, 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]}]}}.
|
1
|
supervisor:start_child(Sup, List)
|
如果启动方法指定为 {M, F, A},则子进程的启动是通过调用 apply(M, F, A++List) 来完成的。