Erlang OTP - Supervisor

2009-04-09
supervisor behaviour用于实现层状监督树机制,包含一套标准的接口函数和追踪错误等的功能,监督者负责启动、停止和监督其子进程的工作,子进程可以是一个工作进程,也可以是一个子监督者,监督者机制的基本理念是通过自动重启子进程而保证其能正常持续的对外提供服务。

监督者的子进程由child specifications列表定义,当监督者启动时,其子进程将按该列表顺序从左到右启动,监督者终止运行时,其子进程将按相反方向关闭,最后终止监督者自己。子进程列表定义如下:
child_spec() = {Id, StartFunc, Restart, Shutdown, Type, Modules}
Id = term()
StartFunc = {M, F, A}
M = F = atom()
A = [term()]
Restart = permanent | transient | temporary
Shutdown = brutal_kill | int()>=0 | infinity
Type = worker | supervisor
Modules = [Module] | dynamic
Module = atom()

其中Id是监督者内部标识该子进程的名称,StartFunc定义了启动该子进程所需调用的函数,这个启动函数必须创建并连接到一个子进程,然后返回{ok, Child}或{ok, Child, Info},Child是子进程的pid,Info会被监督者所忽略;如果子进程因某些原因未能启动可返回ignore;如果发生了错误也可以返回一个{error, Error}。

Restart定义了子进程何时将会被重启:
permanent:无论子进程因何终止都应被重启;
temporary:无须重启子进程;
transient:仅在子进程因非正常原因终止时才重启子进程。

Shutdown定义了子进程应被如何终止:
brutal_kill:使用exit(Child, kill)强制终止;
int()>=0:监督者调用exit(Child, shutdown)告知子进程终止并等待其终止信号,若超过指定时间尚未收到再调用exit(Child, kill)来强制终止;
infinity:如果子进程为一个子监督者,则应将超时时间设定为infinity,以让其有充足时间来终止其子监督树。

Type定义了子进程是一个监督者还是一个工作进程。

监督者对子进程的重启方式(Restart Strategy)有如下几种:
one_for_one:如果一个子进程终止了,则只需重启该子进程;
one_for_all:如果一个子进程终止了,所有其他子进程都将被终止,然后重启所有子进程;
rest_for_one:如果一个子进程终止了,则按启动顺序比其晚启动的其他子进程都将被终止,然后该子进程和被终止掉的其他子进程都会被重启;
simple_one_for_one:是one_for_one的简化版,该监督者的所有子进程都是动态加入的,并且每个子进程类型都相同,都运行一样的代码。

为了避免监督者陷入子进程被不停的终止-重启的死循环,每个监督者都有一个最大重启频度(maximum restart frequency),由参数MaxR和MaxT所决定,如果在MaxT秒内发生多于MaxR次子进程重启,则监督者会终止其所有子进程和它本身。

Callback Module
supervisor behaviour的回调模块僅包含一个回调函数init/1:
init(_Args) ->
{ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}


Starting a Supervisor
监督者通过调用函数supervisor:start_link/2来启动,如下:
supervisor:start_link(MODULE, []).

MODULE为该监督者的回调模块,第二个参数为传入到回调函数init/1里的参数。如果想为该监督者注册个名字,也可以用下面这个函数来启动:
supervisor:start_link({local, Name}, Module, Args) 或
supervisor:start_link({global, Name}, Module, Args)

启动后,监督者会调用回调模块里的init/1函数,并根据该函数的返回值启动其子进程,注意supervisor:start_link这函数不是异步的,要直到所有子进程都启动后才会返回。

Adding a Child Process
监督树可以是静态的,也可以是动态的,我们可以随时加入新的子进程到监督树中:
supervisor:start_child(Sup, ChildSpec)

其中Sup是监督者的pid或注册名,ChildSpec是一个子进程声明。动态增添的子进程和其他子进程一样,但若监督者死掉然后被重新启动后,所有动态添加的子进程都会丢失。

Stopping a Child Process
所有子进程无论动态静态,都可以按照关闭设置被停止运行:
supervisor:terminate_child(Sup, Id)

可用如下函数删除被停止运行的子进程的进程声明:
supervisor:delete_child(Sup, Id)

同样,监督者本身重启后,删除静态子进程的操作将失效。

M-OSCAR | Powered by Blogger | Entries (RSS) | Comments (RSS) | Designed by MB Web Design | XML Coded By Cahayabiru.com