supervisor
模块
supervisor
模块摘要
一般主管的行为。
描述
这个行为模块提供了一个监督者,一个监督称为子进程的其他进程的进程。 一个子进程可以是另一个主管或一个工作进程。 工作进程通常使用gen_event,gen_server或gen_statem行为之一来实现。 使用此模块实施的主管具有一组标准接口功能,并包含用于跟踪和错误报告的功能。 主管人员用于构建称为监督树的分层过程结构,这是一种构建容错应用程序的好方法。 有关更多信息,请参阅OTP设计原则中的管理员行为。
监督者期望在导出预定义的函数集的回调模块中指定要监督的子进程的定义。
除非另有说明,否则如果指定的监控器不存在或指定了错误的参数,则本模块中的所有功能都会失败。
监督原则
主管负责启动、停止和监视其子进程。主管的基本思想是,它必须通过在必要时重新启动子进程来保持子进程的活力。
主管的子女被定义为子规格当主管启动时,子进程将根据此列表从左到右依次启动。当主管终止时,它首先以反转的开始顺序(从右到左)终止其子进程。
主管属性由主管标志定义。主管标志的类型定义如下:
sup_flags() = #{strategy => strategy(), % optional
intensity => non_neg_integer(), % optional
period => pos_integer()} % optional
主管可以通过上图中的strategy
键指定以下重启策略之一:
one_for_one
- 如果一个子进程终止并要重新启动,则只有该子进程受到影响。这是默认的重启策略。
one_for_all
-如果一个子进程终止并将重新启动,则终止所有其他子进程,然后重新启动所有子进程。
rest_for_one
- 如果一个子进程终止并将被重新启动,则子进程的“休息”(即,在启动顺序中的终止子进程之后的子进程)终止。然后终止子进程和所有子进程重新启动后。
simple_one_for_one
-简化one_for_one
监控器,其中所有子进程都是动态添加相同进程类型的实例,即运行相同的代码。
如果指定的管理程序使用此重新启动策略,函数delete_child / 2和restart_child / 2对于simple_one_for_one管理程序无效,并返回{error,simple_one_for_one}。
通过指定孩子的pid()作为第二个参数,函数terminate_child / 2可以用于simple_one_for_one主管下的孩子。 如果使用子规范标识符,terminate_child / 2返回{error,simple_one_for_one}。
因为simple_one_for_one
主管可以有很多子进程,所以他们会异步关闭他们。这意味着孩子们并行地进行清理,因此他们停止的顺序没有被定义。
为了防止主管进入无限循环的子进程终止和重新启动,最大重启强度使用上述映射中用按键强度和周期指定的两个整数值来定义。 假设强度值为MaxR,周期为MaxT,那么如果超过MaxR重新启动发生在MaxT秒内,监督员终止所有子进程,然后自行终止。 在这种情况下,主管本身的终止理由将被关闭。 强度默认为1,周期默认为5。
子规范的类型定义如下:
child_spec() = #{id => child_id(), % mandatory
start => mfargs(), % mandatory
restart => restart(), % optional
shutdown => shutdown(), % optional
type => worker(), % optional
modules => modules()} % optional
旧的元组格式是为了向后兼容而保留的,请参见child_spec()
,但地图是首选。
id
用于由主管在内部识别子规格。该id
关键是强制性的。请注意,这个occations标识符被称为“名称”。尽可能地使用术语“标识符”或“id”,但为了保持向后兼容性,仍然可以找到一些“名称”的出现,例如在错误消息中。
start
定义用于启动子进程的函数调用。它必须是一个模块的功能,参数元组{M,F,A}
使用apply(M,F,A)
。
启动函数必须创建并链接到子进程,并且必须返回{ok,Child}
或{ok,Child,Info}
,Child
子进程的PID以及Info
主管忽略的任何项的位置。
ignore
如果由于某种原因子进程无法启动,在这种情况下,子进程由超级用户保留(除非它是临时子进程),但不存在的子进程被忽略,则启动功能也可以返回。
如果出现问题,该函数也可以返回一个错误元组{error,Error}
。
注意,start_link
不同行为模块的功能满足上述要求。
该start
键是强制性的。
- 重新启动定义何时必须重新启动终止的子进程。 永久的子进程总是重新启动。 临时子进程永远不会重新启动(即使当主管的重启策略是rest_for_one或one_for_all并且兄弟姐妹的死亡导致临时进程终止时)。 临时子进程只有在异常终止时才会重新启动,也就是说,除了正常,关闭或{shutdown,Term}之外的其他退出原因。重启键是可选的。 如果未指定,则默认为永久。
- 关闭定义了子进程必须如何终止。 brutal_kill意味着子进程无条件地使用exit(Child,kill)终止。 一个整数超时值意味着监督者通过调用exit(Child,shutdown)告诉子进程终止,然后等待从子进程返回原因关闭的退出信号。 如果在指定的毫秒数内没有收到退出信号,则子进程将无条件地使用退出(Child,kill)终止。
如果子进程是另一个主管,则关闭时间应设置为无穷大,以使子树有足够的时间关闭。 如果子进程正在工作,也可以将其设置为无穷大。
警告
当子进程是工作者时将关闭时间设置为无穷大时要小心。 因为在这种情况下,监督树的终止取决于子进程,所以必须以安全的方式实施,并且其清理过程必须始终返回。
注意,使用标准OTP行为模块实现的所有子进程都会自动遵守关机协议。
该shutdown
键是可选的。如果未指定,则默认为5000
如果该孩子是类型的,worker
并且其默认为infinity
孩子是类型的supervisor
。
type
指定子进程是主管还是工作者。该type
键是可选的。如果没有指定,则默认为worker
。
- 代码替换期间释放处理程序使用模块来确定哪些进程正在使用某个模块。 作为一个经验法则,如果子进程是一个管理程序gen_server或gen_statem,它将成为一个包含一个元素[Module]的列表,其中Module是回调模块。 如果子进程是带有一组动态回调模块的事件管理器(gen_event),则必须使用值dynamic。 有关版本处理的更多信息,请参阅OTP设计原则中的版本处理。
该modules
键是可选的。如果没有指定,则默认为[M]
,M
从哪里来的孩子的开始{M,F,A}
。
- 在内部,主管还跟踪PID的子类子的过程中,或者不确定如果没有PID存在。
数据类型
child() = undefined | pid()
child_id() = term()
不是a pid()。
child_spec() = #{id := child_id(), start := mfargs(), restart => restart(), shutdown => shutdown(), type => worker(), modules => modules()} | {Id :: child_id(), StartFunc :: mfargs(), Restart :: restart(), Shutdown :: shutdown(), Type :: worker(), Modules :: modules()}
元组格式仅保留向后兼容性。 地图是首选; 上面看到更多细节。
{M :: module(), F :: atom(), A :: [term()] | undefined}
未定义的值(参数列表)只能在supervisor内部使用。 如果子级的重启类型是临时的,则该进程永远不会重新启动,因此不需要存储实参参数列表。 然后存储未定义的值。
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_flags() = #{strategy => strategy(), intensity => integer() >= 0, period => integer() >= 1} | {RestartStrategy :: strategy(), Intensity :: integer() >= 0, Period :: integer() >= 1}
元组格式仅保留向后兼容性。 地图是首选;查看更多细节
sup_ref() = (Name :: atom()) | {Name :: atom(), Node :: node()} | {global, Name :: atom()} | {via, Module :: module(), Name :: any()} | pid()
worker() = worker | supervisor
输出
check_childspecs(ChildSpecs) -> Result
类型
ChildSpecs = [child_spec()]
Result = ok | {error, Error :: term()}
将子规范列表作为参数,如果它们全部在语法上正确,则返回ok,否则返回{error,Error}。
count_children(SupRef) -> PropListOfCounts
类型
SupRef = sup_ref()
PropListOfCounts = [Count]
Count = {specs, ChildSpecCount :: integer() >= 0} | {active, ActiveProcessCount :: integer() >= 0} | {supervisors, ChildSupervisorCount :: integer() >= 0} | {workers, ChildWorkerCount :: integer() >= 0}
返回一个属性列表(请参阅proplists),其中包含主管的子规范和受管进程的以下每个元素的计数:
specs
-结束或存活的子进程。
active
- 由该主管管理的所有正在运行的子进程的计数。对于simple_one_for_one
主管人员,不进行检查以确保每个子进程仍然存在,尽管此处提供的结果可能非常准确,除非主管负载过重。
supervisors
-所有被标记为child_type = supervisor
在规范列表中,无论子进程是否仍然活着。
workers
-child_type = worker
无论子进程是否仍然存活,所有标记为规范列表中子进程的计数。
有关描述SupRef
,请参阅start_child/2
.
delete_child(SupRef, Id) -> Result
类型
SupRef = sup_ref()
Id = child_id()
Result = ok | {error, Error}
Error = running | restarting | not_found | simple_one_for_one
告诉主管SupRef删除由Id标识的子规范。相应的子进程一定不能运行。使用terminate_child/2来终止。
有关SupRef的说明,请参阅 start_child / 2。
如果成功,该函数返回OK。如果由Id标识的子规范存在但相应的子进程正在运行或即将重新启动,则该函数将分别返回{error,running}或 {error,restarting}。如果由Id标识的子规范不存在,则函数返回{error,not_found}。
get_childspec(SupRef, Id) -> Result
类型
SupRef = sup_ref()
Id = pid() | child_id()
Result = {ok, child_spec()} | {error, Error}
Error = not_found
在supervisor SupRef下返回由Id标识的孩子的子规格图。返回的地图包含所有必需键和可选键。
有关SupRef的说明,请参阅 start_child/2。
restart_child(SupRef, Id) -> Result
类型
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标识的子规范相对应的子进程。子规范必须存在,并且相应的子进程不能运行。
请注意,对于临时子类,子终止时会自动删除子女规范;因此,重新启动这样的孩子是不可能的。
有关SupRef的说明,请参阅 start_child/2。
如果由Id标识的子规范不存在,则函数返回{error,not_found}。如果子规范存在但相应的进程已经在运行,则函数返回{error,running}。
如果子进程启动函数返回{ok,Child} 或{ok,Child,Info},则将pid添加到主管,并且该函数返回相同的值。
如果子进程启动函数返回忽略,则pid仍然设置为undefined,函数返回{ok,undefined}。
如果子进程启动函数返回错误元组或错误值,或者如果失败,则函数返回{error,Error},其中Error是包含有关错误信息的术语。
start_child(SupRef, ChildSpec) -> startchild_ret()
类型
SupRef = sup_ref()
ChildSpec = child_spec() | (List :: [term()])
startchild_ret() = {ok, Child :: child()} | {ok, Child :: child(), Info :: term()} | {error, startchild_err()}
startchild_err() = already_present | {already_started, Child :: child()} | term()
向子管理员SupRef动态添加子规范,该子管理员 启动相应的子进程。
SupRef可以是以下任何一种:
- The pid
Name
,如果主管是在本地注册的
{Name,Node}
,如果监控程序在另一个节点上本地注册。
{global,Name}
,如果主管在全球注册
{via,Module,Name}
,如果主管是通过另一个流程注册表注册的。
ChildSpec
必须是有效的子规范(除非主管是simple_one_for_one
主管;见下文)。子进程通过使用子规范中定义的启动函数来启动。
对于simple_one_for_one管理程序,使用Module:init / 1中定义的子规范,而ChildSpec必须是List的任意列表。 然后通过将List附加到现有的启动函数参数,即通过调用apply(M,F,A ++ List)开始子进程,其中{M,F,A}是子规范中定义的启动函数。
- 如果已经存在具有指定标识符的子规范,
ChildSpec
,则该函数将返回{error,already_present}
或{error,{already_started,Child}}
,取决于相应的子进程是否正在运行。
- 如果子进程启动函数返回
{ok,Child}
或{ok,Child,Info}
,将子规范和PID添加到监控器,函数返回相同的值。
- 如果子进程启动函数返回
ignore
,则将子进程规范添加到主管(除非主管是simple_one_for_one
主管,见下文),PID设置为undefined
,并且函数返回{ok,undefined}
。
对于simple_one_for_one
主管,当子进程启动函数返回时ignore
,函数返回{ok,undefined}
并且没有任何子项被添加到管理程序中。
如果子进程启动函数返回错误元组或错误值,或者如果失败,则丢弃子规范,并返回函数{error,Error}
,其中Error
是一个包含有关错误和子规范的信息的术语。
start_link(Module, Args) -> startlink_ret()
start_link(SupName, Module, Args) -> startlink_ret()
类型
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}
,主管在本地注册为Name
使用register/2
。
- 如果
SupName={global,Name}
,主管在全球范围内注册为Name
使用global:register_name/2
。
- 如果SupName = {via,Module,Name},则使用Module代表的注册表将管理员注册为Name。 Module回调必须导出函数register_name/2,unregister_name/1和send/2,它们的行为必须与全局中的相应函数相同。 因此,{via,global,Name}是一个有效的参考。
如果没有提供名称,则不注册主管。
Module
回调模块的名称。
Args
是作为参数传递给Module:init/1
的任何术语。
- 如果主管及其子进程创建成功(也就是说,如果所有的子进程启动函数返回
{ok,Child}
,{ok,Child,Info}
或ignore
),该函数返回{ok,Pid}
,这里Pid
是主管的PID。
- 如果已经存在具有指定的进程
SupName
,则函数返回{error,{already_started,Pid}}
,其中Pid
为进程的pid
- 如果Module:init/1返回忽略,该函数也会返回忽略,并且管理程序以reason正常结束。
- 如果
Module:init/1
失败或返回不正确的值,则此函数返回{error,Term}
,其中Term
是包含有关错误的信息的术语,并且主管因Term
而终止。
- 如果任何子进程启动函数失败或返回错误元组或错误值,监督程序首先终止所有已启动的具有reason关闭的子进程,然后自行终止并返回{error,{shutdown,Reason}}。
terminate_child(SupRef, Id) -> Result
类型
SupRef = sup_ref()
Id = pid() | child_id()
Result = ok | {error, Error}
Error = not_found | simple_one_for_one
T告诉主管SupRef终止指定的子类。
如果主管不是simple_one_for_one,那么 Id必须是子规范标识符。该过程(如果有的话)被终止,并且除非它是临时子女,否则子女规范由主管保存。这个子进程稍后可以由主管重新启动。子进程也可以通过调用restart_child/2来显式重启 。使用 delete_child/2 删除子规范。
如果孩子是临时的,那么只要过程终止,孩子的规格就会被删除。这意味着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}。
有关SupRef的说明,请参阅 start_child/2。
which_children(SupRef) - > [{Id,Child,Type,Modules}]
类型
SupRef = sup_ref()
Id
-正如子规范或规定undefined
的simple_one_for_one
主管。
Child
-相应子进程的PID,如果进程即将重新启动,则重新启动原子;如果没有此进程,则为未定义。
Type
- 如子规范中所定义。
Modules
- 如子规范中所定义。
回调函数
必须从supervisor
回调模块。
输出
Module:init(Args) -> Result
类型
每当管理员开始使用时start_link/2,3
,新功能就会调用该功能以查找重启策略,最大重启强度以及子规格。
Args
是Args
提供给启动函数的参数。
SupFlags是主管标志,用于定义主管的重启策略和最大重启强度。 [ChildSpec]是一个有效的子规范列表,用于定义主管必须启动和监视哪个子进程。 请参阅前面“监督原则”部分的讨论。
请注意,当重新启动策略为simple_one_for_one时,子规范列表必须是仅包含一个子规范的列表。 (子规范标识符被忽略。)然后在初始化阶段没有启动子进程,但是所有的子进程都假定使用start_child / 2动态启动。
该函数也可以返回ignore
。
注意这个函数也可以作为代码升级过程的一部分被调用。 因此,该功能不会有任何副作用。 有关主管代码升级的更多信息,请参见更改OTP设计原则中的主管。
另见
gen_event(3)
,gen_statem(3)
,gen_server(3)
,sys(3)
本文档系腾讯云开发者社区成员共同维护,如有问题请联系 cloudcommunity@tencent.com