概要:
通用监督者行为
描述:
一个实现监督者的行为模块,一个监督被称为子进程的其它进程的进程。一个子进程可以是另一个监督者或工作者进程。工作者进程通常的实现使用gen_event,gen_fsm和gen_server中的行为。监督者使用这个模块实现一组标准的接口函数和包括跟踪和错误报告的功能。监督者被用来构建称为监控树的分层进程结构,一个构建容错应用的很好的方式。参考OTP设计原理获得更多信息。
监督者设定哪些子进程被监督的定义,位于导出一组预定义函数的回调模块。
除非另作说明,如果指定的supervisor不存在或给出错误参数,该模块所有函数会失败。
监督原则:
监督者负责启动、停止和监控它的子进程。监督者的基本思想是,它应该保证它的子进程活着,必要时重启它们。
监督者的子进程被定义为子规范列表。当监督者被启动时,子进程依据这个列表从左到右按顺序被启动。当监督者终止时,它首先按相反的启动顺序,从右到左终止它的子进程。
监督者可以有以下重启策略之一:
one_for_one - 如果子进程终止,应重新启动,只有子进程受到影响。
one_for_all - 如果子进程终止,应重新启动,所有其它子进程终止,那么所有的子进程被启动。
rest_for_one - 如果子进程终止,应重新启动,后面的子进程,也就是说,按启动顺序,被终止进程的后面的子进程被终止。那么,终止进程和它后面所有的子进程被重启。
simple_one_for_one - 简化的one_for_one监督者,其中,所有的子进程被动态添加同样进程类型的实例,也就是,运行相同的代码。
对于simple_one_for_one监督者,函数delete_child/2 和 restart_child/2是有效的。如果指定的监督者使用该重启策略,会返回{error,simple_one_for_one}。
在simple_one_for_one监督者下,通过给定子进程的进程号作为第二个参数,函数terminate_child/2可被用于子进程。如果子规范标识符被使用,terminate_child/2将返回{error,simple_one_for_one}。
因为一个simple_one_for_one监督者可以有很多的子进程,它在同一时间将它们关闭。所以,它们被停止的顺序没有被定义。出于同样的原因,它可能有一个开销对于关闭策略。
为防止监督者进入子进程终止和重启的无限循环,使用两个整数MaxR 和 MaxT定义最大重启频率。如果在MaxT秒重启超过MaxR发生,监督者终止所有子进程,随后终止它自己。
这是子规范的类型定义:
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定义函数调用用于启动子进程。它应该是一个模块-函数-参数元组{M,F,A}用作apply(M,F,A)。
启动函数必须创建和链接到子进程,应该返回{ok,Child}或{ok,Child,Info},其中Child是子进程的进程号,Info是任意term被监督者忽略。
如果子进程因为一些原因不能启动,启动函数也可以返回ignore,这种情况,子规范将会被监督者保存(除非它是temporary进程),但不存在的子进程将被忽略。
如果出现错误,函数也可能返回错误元组{error,Error}。
请注意,start_link函数,不同的行为模块满足上述的要求。
Restart定义什么时候终止的子进程应该被重启。一个permanent子进程应该总是被重启,一个temporary子进程从不被重启(即使当监督者的重启策略是rest_for_one或one_for_all和兄弟进程的挂掉导致temporary进程被终止),和一个transient子进程应该被重启仅当它异常终止,也就是说,除过normal,shutdown或{shutdown,Term}的其它退出原因。
Shutdown定义子进程应该如何被终止。brutal_kill意味着子进程将被无条件终止使用exit(Child,kill)。一个整数超时值意味着监督者将告诉子进程通过调用exit(Child,shutdown)来终止,然后以原因shutdown等待来自子进程的退出信号。如果在指定数目毫秒内没有收到退出信号,子进程使用exit(Child,kill)被无条件终止。
如果子进程是另一个监督者,Shutdown应该被设置成infinity,给子树充足的时间关闭。如果子进程是工作者,它也被允许设置成infinity。
警告:小心设置shutdown策略为infinity,当子进程是一个工作者。因为,在这种情形下,监控树的终止取决于子进程,它必须以安全的方式实现,它的清理过程必须返回。
请注意,所有子进程自动使用标准OTP行为模块实现依据关闭协议。
Type指定子进程是监督者还是工作者。
Modules被版本处理程序使用,在代码替换期间用于确定哪些进程使用哪些模块。作为一个经验法则,Modules应该一个元素列表[Module],其中,Module是回调模块,如果子进程是一个supervisor, gen_server 或 gen_fsm。如果子进程是一个带有回调模块集合的事件管理器(gen_event),Modules应该是dynamic。关于版本控制的更多信息参考OTP设计原则。
内部地,监督者也跟踪子进程的进程号,或undefined如果没有进程号存在。
数据类型:
child() = undefined | pid() child_id() = term() %% 不是pid() child_spec() = {Id :: child_id(), StartFunc :: mfargs(), Restart :: restart(), Shutdown :: shutdown(), Type :: worker(), Modules :: modules()} mfargs() = {M :: module(), F :: atom(), A :: [term()] | undefined} %% 如果Restart是temporary,A的值为undefined。 modules() = [module()] | dynamic restart() = permanent | transient | temporary shutdown() = brutal_kill | timeout() strategy() = one_for_all | one_for_one | rest_for_one | simple_one_for_one sup_ref() = (Name :: atom()) | {Name :: atom(), Node :: node()} | {global, Name :: atom()} | {via, Module :: module(), Name :: any()} | pid() worker() = worker | supervisor
导出:
start_link(Module, Args) -> startlink_ret() start_link(SupName, Module, Args) -> startlink_ret() Types: SupName = sup_name() Module = module() Args = term() startlink_ret() = {ok, pid()} | ignore | {error, startlink_err()} startlink_err() = {already_started, pid()} | {shutdown, term()} | term() sup_name() = {local, Name :: atom()} | {global, Name :: atom()} | {via, Module :: module(), Name :: any()}
创建一个监督者进程作为监控树的一部分,在其它方面,函数将确保监督者链接到调用者进程(它的监督者)。
被创建的监督者进程调用Module:init/1找出重启策略、最大启动频率和子进程。为确保同步启动过程,start_link/2,3不会返回直到Module:init/1已经返回且所有子进程已被启动。
如果SupName={local,Name},supervisor使用register/2被注册为本地的Name。如果SupName={global,Name},supervisor使用global:register_name/2被注册为全局的Name。如果没有提供Name,supervisor不会被注册。如果SupName={via,Module,ViaName},supervisor将会用Module代表的注册表注册。Module回调应该导出函数register_name/2, unregister_name/1, whereis_name/1 和 send/2,它们表现得像global模块对应的函数。因此,{via,global,Name}是一个有效地引用。
如果没有提供名称,监督者不会注册。
Module是回调模块的名称。
Args是一个任意term,作为参数传递给Module:init/1。
如果监督者和它的子进程被成功创建,(也就是说,如果所有子进程启动函数返回{ok,Child}, {ok,Child,Info}, 或 ignore)函数返回{ok,Pid},其中Pid是监督者的进程号。如果已存在指定SupName的进程,函数返回{error,{already_started,Pid}},其中,Pid是那个进程的进程号。
如果Module:init/1返回ignore,该函数也返回ignore,而监督者以原因normal终止。如果Module:init/1失败或返回不正确的值,该函数返回{error,Term},其中,Term是包含关于错误信息的Term,监督者以原因Term终止。
如果任何子进程启动函数是失败或返回一个错误元组或一个错误值,监督者首先将以原因shutdown终止所有已启动的进程,随后终止它自己,然后返回{error, {shutdown, Reason}}。
start_child(SupRef, ChildSpec) -> startchild_ret() Types: SupRef = sup_ref() ChildSpec = child_spec() | (List :: [term()]) child_spec() = {Id :: child_id(), StartFunc :: mfargs(), Restart :: restart(), Shutdown :: shutdown(), Type :: worker(), Modules :: modules()} startchild_ret() = {ok, Child :: child()} | {ok, Child :: child(), Info :: term()} | {error, startchild_err()} startchild_err() = already_present | {already_started, Child :: child()} | term()
动态增加一个子规范到监督者SuperRef,它启动对应的子进程。
ServerRef可以是:
- 进程号;
- Name,supervisor被本地注册的名称;
- {Name,Node},supervisor在其它节点被本地注册;
- {global,Name},supervisor被全局注册;
- {via,Module,ViaName},supervisor通过替代的进程注册表注册。
ChildSpec应该是有效的子进程(除非该监督者是一个simple_one_for_one的监督者,看下面)。子进程将会使用定义在子规范的启动函数启动。
如果是simple_one_for_one监督者的情况下,定义在Module:init/1的子规范将被使用,ChildSpec应该是一个任意term列表。子进程将被启动通过添加List到已存在的启动函数参数,也就是说,通过调用apply(M, F, A++List),其中,{M,F,A}是定义在子规范的启动函数。
如果已经存在一个指定id的子规范,ChildSpec被丢弃,函数返回{error,already_present} 或 {error,{already_started,Child}},取决于对应的子进程是否运行。
如果子进程启动函数返回{ok,Child} 或 {ok,Child,Info},子规范和进程号被添加到监督者,函数返回相同的值。
如果子进程启动函数返回ignore,子规范被添加到监督者,进程号设置为undefined,函数返回{ok,undefined}。
如果子进程启动哈数返回一个错误元组或一个错误值, 或它失败,子规范被丢弃,函数返回{error,Error},其中Error是一个包含关于错误和子规范信息的term。
terminate_child(SupRef, Id) -> Result Types: SupRef = sup_ref() Id = pid() | child_id() Result = ok | {error, Error} Error = not_found | simple_one_for_one
告诉监督者SupRef终止给定的子进程。
如果监督者不是simple_one_for_one,Id必须是子规范标识符。进程被终止,如果有,除非它是temporary子进程,子规范被监督者保存。子进程随后可能被监督者重启。子进程也可能显式的通过调用restart_child/2被重启。使用delete_child/2移除子规范。
如果子进程是temporary,进程一终止,子规范就被删除。这意味着delete_child/2没有意义,restart_child/2无法用于这些进程。
如果监督者是simple_one_for_one,Id必须是子进程的pid(),如果指定的进程活着,但不是给定监督者的子进程,函数将返回{error,not_found}。如果给定子规范标识,而不是pid(),函数将返回{error,simple_one_for_one}。
如果成功,函数返回ok。如果没有指定Id的子规范,函数返回{error,not_found}。
参考start_child/2了解SupRef的描述。
restart_child(SupRef, Id) -> Result Types: SupRef = sup_ref() Id = child_id() Result = {ok, Child :: child()} | {ok, Child :: child(), Info :: term()} | {error, Error} Error = running | restarting | not_found | simple_one_for_one | term()
告诉监督者SupRef重启一个子进程根据子规范标识符Id。子规范必须存在,对应子进程必须没有在运行。
请注意temporary子进程,当子进程终止,子规范自动被删除。随后,它不能重启这些子进程。
参看start_child/2了解SupRef的描述。
如果子规范标识Id不存在,函数返回{error,not_found}。如果子规范存在但对应进程已经运行,函数返回{error,running}。
子进程启动函数返回{ok,Child} 或 {ok,Child,Info},进程号被添加到监督者,函数返回相同的值。
子进程启动函数返回ignore,进程号仍然设置为undefined,函数返回{ok,undefined}。
子进程启动函数返回一个错误元组或一个错误值,或它失败,函数返回{error,Error},其中,Error是一个包含错误信息的term。
which_children(SupRef) -> [{Id, Child, Type, Modules}] Types: SupRef = sup_ref() Id = child_id() | undefined Child = child() | restarting Type = worker() Modules = modules()
返回一个新创建列表,含有所有子规范和归属于监督者SupRef的子进程。
请注意,在低内存状态下,监督大量子进程,调用该函数可能导致内存不足的异常。
参考start_child/2了解SupRef的描述。
对于每个子规范/进程给出的信息是:
- Id - 子规范中定义或在simple_one_for_one监督者情况下为undefined;
- Child - 对应子进程的进程号,函数将被重启为restarting或没有该进程为undefined;
- Type - 定义在子规范;
- Modules - 定义在子规范。
count_children(SupRef) -> PropListOfCounts Types: SupRef = sup_ref() PropListOfCounts = [Count] Count = {specs, ChildSpecCount :: integer() >= 0} | {active, ActiveProcessCount :: integer() >= 0} | {supervisors, ChildSupervisorCount :: integer() >= 0} | {workers, ChildWorkerCount :: integer() >= 0}
返回一个属性列表,它包含监督者子规范的下列元素和被管理的进程的数量:
- specs - 子进程活的或死的总数量;
- active - 所有被监督者管理的激活的运行的子进程数量;
- supervisors - 在规范列表被标记为child_type = supervisor的所有子进程数量,不管子进程是否活着;
- workers - 在规范列表被标记为child_type = worker的所有子进程数量,不管子进程是否活着;
check_childspecs(ChildSpecs) -> Result Types: ChildSpecs = [child_spec()] Result = ok | {error, Error :: term()}
该函数需要一个子规范列表作为参数,如果他们在语法上都正确,返回ok,否则返回{error,Error}。
回调函数:
Module:init(Args) -> Result Types: Args = term() Result = {ok,{{RestartStrategy,MaxR,MaxT},[ChildSpec]}} | ignore RestartStrategy = strategy() MaxR = integer()>=0 MaxT = integer()>0 ChildSpec = child_spec()
无论何时使用supervisor:start_link/2,3 监督者被启动,函数被一个新的进程调用找出重启策略、最大重启频率和子规范。
Args是提供给启动函数的参数。
RestartStrategy是重启策略,MaxR 和 MaxT定义监督者的最大重启频率。 [ChildSpec]是一组有效地子规范,它定义哪些进程监督者应该启动和监控。参看上面关于监督原则的讨论。
请注意,当重启策略为simple_one_for_one,子规范列表必须只含有一个子规范列表(ID被忽略)。在初始化期间,没有子进程随后被启动,但是所有子进程被设定使用supervisor:start_child/2来动态启动。
函数也可能返回ignore。
翻译有问题的地方,请大家指正。