体育365网址一个子经过能够是另3个监督者或工作者经过,创造3个erlang节点后

gen_server

概要

erlang应用脚本stop分析

实际上那篇小说的名字应该是何许安全关闭erlang应用尤其不易。

概要
  通用服务器行为
描述

  通用监督者行为

erlang应用脚本生成

行使rebar工具,创制三个erlang节点后,
<pre>
./rebar create-node nodeid=hook_heroes
</pre>
然后在rel目录里面,执行打包命令
<pre>
./rebar generate
</pre>
会变动完整的利用包,目录如下:
<pre>
bin erts-6.0 lib log releases
</pre>
bin里面,有1个开发银行脚本名字和节点名字如出壹辙的,那里是hook_heroes

停止服务的时候,近来选拔
<pre>
./hook_heroes stop
</pre>

  行为模块落成服务器的客户端-服务器涉及。二个通用的服务器进度使用那几个模块将落到实处①组正式的接口功用,包罗跟踪和错误报告功用。它也契合OTP进程监察和控制树。明白越多音讯参考OTP设计基准。

 

对于hook_heroes stop分析

hook_heroes stop调用如下
<pre>
%%Tell nodetool to initiate a stop
$NODETOOL stop
ES=$?
if [ “$ES” -ne 0 ]; then
exit $ES
fi
</pre>
这里的nodetool来自
<pre>
NODETOOL=”$ERTS_PATH/escript $ERTS_PATH/nodetool
</pre>
即erts包上边包车型地铁nodetool脚本,传入的参数stop
nodetool是二个escript脚本,功效正是“Helper Script for interacting with
live nodes”

<pre>
case RestArgs of
[“getpid”] ->
io:format(“~p\n”,
[list_to_integer(rpc:call(TargetNode, os, getpid, []))]);
[“ping”] ->
io:format(“pong\n”);
[“stop”] ->
io:format(“~p\n”, [rpc:call(TargetNode, init, stop, [], 60000)]);
…….
</pre>

能够见见,直接运用的是rpc:call()方法:调用TargetNode的init模块的stop方法,传入的参数为[],上面来探视init模块的stop方法。

  gen_server假定全部特定部分位于四个回调模块,它导出的一组预约义的成效。行为函数和回调函数的涉及可以证实如下:

描述

init模块的stop()方法调用

init 模块的文书档案给的演说是:“Coordination of System Startup”,
stop方法的笺注是:
<pre>
All applications are taken down smoothly, all code is unloaded, and all
ports are closed before the system terminates
</pre>
旗帜显然正是用来系统关闭的,关键是亟需探视他是怎么关闭系统的。

  gen_server module Callback module
  —————– —————
  gen_server:start_link —–> Module:init/1

  3个落实监督者的表现模块,3个监督检查被称为子进度的其余进度的经过。二个子进度能够是另一个监督者或工小编经过。工作者经过平常的贯彻应用gen_event,gen_fsm和gen_server中的行为。监督者使用这一个模块达成1组正式的接口函数和总结跟踪和错误报告的效益。监督者被用来创设称为监察和控制树的分支进度组织,一个营造容错应用的很好的方法。参考OTP设计原理得到越来越多音信。

函数入口:

<pre>
stop() -> init ! {stop,stop}, ok.
</pre>
给init模块发送温馨发送3个{stop,stop}音信,

init本身循环接收信息
<pre>
loop(State) ->
receive
{‘EXIT’,Pid,Reason} ->
Kernel = State#state.kernel,
terminate(Pid,Kernel,Reason), %% If Pid is a Kernel pid, halt()!
loop(State);
{stop,Reason} ->
stop(Reason,State);
{From,fetch_loaded} -> %% The Loaded info is cleared in
Loaded = State#state.loaded, %% boot_loop but is handled here
From ! {init,Loaded}, %% anyway.
loop(State);
{From, {ensure_loaded, _}} ->
From ! {init, not_allowed},
loop(State);
Msg ->
loop(handle_msg(Msg,State))
end.
</pre>

分外到{stop,Reason},进入stop(Reason,State)那里调用,Reason为stop,
来打那里
<pre>
stop(Reason,State) ->
BootPid = State#state.bootpid,
{_,Progress} = State#state.status,
State1 = State#state{status = {stopping, Progress}},
clear_system(BootPid,State1),
do_stop(Reason,State1).
</pre>
主要看下clear_system函数和do_stop函数

  gen_server:call
  gen_server:multi_call —–> Module:handle_call/3

 

clear_system()函数

clear_system()那里的法力就是关闭虚拟机中的进度,只用多少个函数调用
<pre>
clear_system(BootPid,State) ->
Heart = get_heart(State#state.kernel), %A
shutdown_pids(Heart,BootPid,State), %B
unload(Heart). %C
</pre>

A和C都是在拍卖erlang运维参数heart,其意思在vm.args有认证
<pre>
Heartbeat management; auto-restarts VM if it dies or becomes
unresponsive
(Disabled by default..use with caution!)
-heart
</pre>
相似景况下,不应用-heart
小编们那边只看shutdown_pids()如何做的。

  gen_server:cast
  gen_server:abcast     —–> Module:handle_cast/2

  监督者设定哪些子进度被监察和控制的概念,位于导出壹组预约义函数的回调模块。

shutdown_pids()函数

<pre>
shutdown_pids(Heart,BootPid,State) ->
Timer = shutdown_timer(State#state.flags),
catch shutdown(State#state.kernel,BootPid,Timer,State),
kill_all_pids(Heart), % Even the shutdown timer.
kill_all_ports(Heart),
flush_timout(Timer).
</pre>

这里首先关闭定时器,然后关门kernel进度,然后再kill别的的进程。

关闭kernel进程
<pre>
%%
%% A kernel pid must handle the special case message
%% {‘EXIT’,Parent,Reason} and terminate upon it!
%%
shutdown_kernel_pid(Pid, BootPid, Timer, State) ->
Pid ! {‘EXIT’,BootPid,shutdown},
shutdown_loop(Pid, Timer, State, []).
</pre>

  -               —–> Module:handle_info/2

 

什么是erlang的kernel进程?

那句话是主要: A kernel pid must handle the special case message and
terminate upon it!
那么如何是kernel进度呢?
看下bin/start.script
<pre>

{kernelProcess,heart,{heart,start,[]}},
{kernelProcess,error_logger,{error_logger,start_link,[]}},
{kernelProcess,application_controller,
{application_controller,start,
[{application,kernel,

</pre>

这一个带kernelProcess标签的历程都以, 特别是application!

来自http://blog.yufeng.info/archives/1411

故supervisor_tree收到的是{‘EXIT’,BootPid,shutdown}

kill其他的历程:
<pre>
kill_all_pids(Heart) ->
case get_pids(Heart) of
[] ->
ok;
Pids ->
kill_em(Pids),
kill_all_pids(Heart) % Continue until all are really killed.
end.
</pre>
说起底跟下去,使用的是
<pre>
exit(Pid,kill)
</pre>
向各样进度发送kill消息。

  -               —–> Module:terminate/2

  除非另作注解,借使钦赐的supervisor不设有或提交错误参数,该模块全体函数会失利。

supervisor terminate方法

supervisor中的terminate()方法如下:
<pre>
-spec terminate(term(), state()) -> ‘ok’.

terminate(_Reason, #state{children=[Child]} = State) when
?is_simple(State) ->
terminate_dynamic_children(Child,
dynamics_db(Child#child.restart_type,
State#state.dynamics),
State#state.name);
terminate(_Reason, State) ->
terminate_children(State#state.children, State#state.name).
</pre>

分为simple_one_for_one和非simple_one_for_one二种意况。
terminate_dynamic_children()方法:
<pre>

EStack = case Child#child.shutdown of
brutal_kill ->
?SETS:fold(fun(P, _) -> exit(P, kill) end, ok, Pids),
wait_dynamic_children(Child, Pids, Sz, undefined, EStack0);
infinity ->
?SETS:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids),
wait_dynamic_children(Child, Pids, Sz, undefined, EStack0);
Time ->
?SETS:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids),
TRef = erlang:start_timer(Time, self(), kill),
wait_dynamic_children(Child, Pids, Sz, TRef, EStack0)
end,

</pre>

能够看来ChildSpec中的ShowDown字段的安装对于关闭子进程的影响:
brutal_kill:发送kill音信,这些音讯是不能捕捉的。就算假设worker设置了process_flag(trap_exit,
true),如故不会接受{‘EXIT’,_FROM,REASON}那几个信息;
infinity和Time都会向监督的worker进度发送shutdown复信号,这里worker做了
process_flag(trap_exit,
true),自然会收取{‘EXIT’,_FROM,REASON}。唯一的区分是infinity会一向守候,Time会设置三个过期:若是超时过了,那么supervisor会发送kill实信号,直接杀死。
依照地点的辨析,简单和erlang文书档案中对此gen_server terminate()方法
<pre>
If the gen_server is part of a supervision tree and is ordered by its
supervisor to terminate, this function will be called with
Reason=shutdown if the following conditions apply:

the gen_server has been set to trap exit signals, and
the shutdown strategy as defined in the supervisor’s child specification
is an integer timeout value, not brutal_kill.
</pre>

  -               —–> Module:code_change/3

 

supervisor什么日期调用terminate()方法

谈起底二个标题来了,supervisor何时调用terminate()方法?以前分析到,关闭kernel进度的时候,supervisor监察和控制树进程会收取来自BootPid的{‘EXIT’,BootPid,shutdown}讯息。大家知晓supervisor实际上1个gen_server,那么去探访她的handle_info()方法好了。

<pre>
-spec handle_info(term(), state()) ->
{‘noreply’, state()} | {‘stop’, ‘shutdown’, state()}.

handle_info({‘EXIT’, Pid, Reason}, State) ->
case restart_child(Pid, Reason, State) of %重启child
{ok, State1} -> %A
{noreply, State1};
{shutdown, State1} -> %B
{stop, shutdown, State1}
end;

handle_info(Msg, State) ->
error_logger:error_msg(“Supervisor received unexpected message:
pn”,
[Msg]),
{noreply, State}.
</pre>

那边代码鲜明都以handle_info
child发送过来的时限信号,调用restart_child()。在跟踪restart_child()进去,也一向不见到原因:因为传播Pid并不是Child,而是BootPid,总是会走到A分支,也便是说不会调用terminate方法。那里陷入困境。
新兴阅读了supervisor文档,发现居然未有terminate()方法的辨证,再一次陷入困境。
最终,想起supervisor实际上二个gen_server,应该去探访gen_server()文书档案对于terminate()方法地注解。
<pre>

Even if the gen_server is not part of a supervision tree, this function
will be called if it receives an ‘EXIT’ message from its parent. Reason
will be the same as in the ‘EXIT’ message.

</pre>
此地表达,只要gen_server收到了来自parent的’EXIT’
message,terminate()方法就会调用。符合此前分析地:
<pre>
{‘EXIT’,BootPid,shutdown}
</pre>
有关BootPid和SuperVisor是不是是parent关系,那里权且没时间商量:然而肯定会是,不然,顶层的sup一定要有人布告关闭啊,而且BootPid从命名来看,十二分有十分大大概。那里留三个坑前边填上,首就算init:start()的启航。

  倘诺2个回调函数失利或回到三个不当的值,gen_server将会终止。

  监督规则:

其它
  • 事先代码中的player进度的child_spec的show_down写的是brutal_kill,那里肯定写错了;那么应用关闭的时候,自然不会调用terminate方法
  • Erlang OTP之terminate
    深入剖析
    那篇小说是基于erlang
    1四A版本的,他建议利用one_for_one。原因非常的粗略,erlang
    14A中,supervisor的terminate()函数如下
    <pre>
    terminate(_Reason, State) ->
    terminate_children(State#state.children, State#state.name),
    ok.
    </pre>
    对于一七本子,能够看到,那里未有处理单独simple_one_for_one的情况。因为simple_one_for_one和one_for_one的child音信在supervisor里面储存的是不均等的:前者child存储在dynamics属性,
    后者存款和储蓄在children属性。erlang
    14A的版本只处理了children里面包车型地铁child,对于simple_one_for_one的child直接未有处理。
    对于那篇小说的试验,笔者在融洽电脑上也做了尝试,确实和他的结果不1样。

  gen_server处理系统新闻,它们记录在sys。sys模块能够用来调节gen_server。

  监督者负责运转、甘休和监察它的子进程。监督者的骨干思索是,它应有保险它的子进程活着,须求时重启它们。

参考资料

  请注意,gen_server并不活动地破获退出复信号,这几个必须旗帜显明地在回调模块运营。

 

  除非另作表明,假如钦命的gen_server不设有或提交错误参数,该模块全体函数会战败。

  监督者的子进度被定义为子规范列表。当监督者被运维时,子进度依据这么些列表从左到右按梯次被启动。当监督者终止时,它首先按相反的启航顺序,从右到左终止它的子进程。

  假诺三个回调函数钦命”hibernate”而不是超时值,该gen_server进度会跻身休眠。那说不定是卓有成效的,如若服务器测度闲置非常短壹段时间。可是这几个效应应该小心使用,使用休眠意味着至少有五个污源收集(休眠又高效唤起),不是您想做的工作里面,每一回调用三个抗尘走俗的服务器。

 

导出

  监督者能够有以下重启策略之一:

start_link(Module, Args, Options) -> Result
start_link(ServerName, Module, Args, Options) -> Result
  Types:
    ServerName = {local,Name} | {global,GlobalName} | {via,Module,ViaName}
      Name = atom()
      GlobalName = ViaName = term()
    Module = atom()
    Args = term()
    Options = [Option]
      Option = {debug,Dbgs} | {timeout,Time} | {spawn_opt,SOpts}
      Dbgs = [Dbg]
        Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}
      SOpts = [term()]
    Result = {ok,Pid} | ignore | {error,Error}
      Pid = pid()
      Error = {already_started,Pid} | term()

  one_for_one –
如若实进度终止,应重新启航,只有子进程遭到震慑。

  成立二个gen_server进度作为监察和控制树的壹有个别。函数应该直接或直接地被supervisor调用。它将确定保证gen_server在别的地点被链接到supervisor。

  one_for_all –
假使实进度终止,应重新起动,全数其余子进度终止,那么富有的子进程被运维。

  gen_server进度调用Module:init/一实行开首化。为确认保障同步运维进程,start_link/3,四甘休Module:init/1执行到位才能回去。

  rest_for_one –
假若实进度终止,应重新起动,后边的子进度,也正是说,按运维顺序,被终止进程的前边的子进度被结束。那么,终止进度和它背后全数的子进度被重启。

  如果ServerName={local,Name},gen_server使用register/2被登记为本土的Name。要是ServerName={global,GlobalName},gen_server使用global:register_name/二被注册为大局的GlobalName。纵然未有提供Name,gen_server不会被登记。若是ServerName={via,Module,ViaName},gen_server将会用Module代表的注册表注册。Module回调应该导出函数register_name/2,
unregister_name/1, whereis_name/一 和
send/二,它们表现得像global模块对应的函数。因而,{via,global,GlobalName}是二个实惠地引用。

  simple_one_for_one –
简化的one_for_one监督者,在那之中,全部的子进度被动态增进相同进度类型的实例,也正是,运行相同的代码。

  Module是回调模块的名号。

 

  Args是任意term,作为参数字传送递给Module:init/一。

  对于simple_one_for_one监督者,函数delete_child/2 和
restart_child/贰是行得通的。借使内定的监督者使用该重启策略,会重回{error,simple_one_for_one}。

  假若选拔是{timeout,Time},gen_server被允许开销Time阿秒起首化,只怕它将被结束,并且运转函数再次回到{error,timeout}。
  尽管选拔是{debug,Dbgs},对于Dbgs里的各种条目,对应的sys函数将会被调用。参见sys。
  假使选用是{spawn_opt,SOpts},SOpts将被视作挑选列表传递给spawn_opt内建函数,它被用来发出gen_server。

 

  如果gen_server被成功开创和初步化,函数再次回到{ok,Pid},个中Pid是gen_server的进度号。假如已经存在使用钦赐ServerName的长河,函数再次来到{error,{already_started,Pid}},在这之中,Pid是特别进程的进程号。

  在simple_one_for_one监督者下,通过给定子进度的进程号作为第叁个参数,函数terminate_child/二可被用于子进度。假使实规范标识符被应用,terminate_child/2将返回{error,simple_one_for_one}。

  假诺Module:init因为Reason战败,函数再次回到{error,Reason}。假设Module:init/一重回{stop,Reason}
或 ignore,进度被终止并且函数会独家重临{error,Reason} 或 ignore。

 

start(Module, Args, Options) -> Result
start(ServerName, Module, Args, Options) -> Result
  Types:
    同 start_link/3,4。

  因为七个simple_one_for_one监督者能够有许多的子进程,它在同一时半刻间将它们关闭。所以,它们被终止的次第没有被定义。出于同样的原故,它恐怕有2个花费对于关闭策略。

  创造2个独立的gen_server进程,也就是,gen_server不是监察和控制树的壹部分还要未有监督进程。

 

  参看start_link/3,四打探参数和重临值的描述。

  为严防监督者进入子进度终止和重启的最佳循环,使用五个整数马克斯Tiguan 和
马克斯T定义最大重启频率。借使在马克斯T秒重启超越马克斯卡宴产生,监督者终止全数子进程,随后停下它和谐。

call(ServerRef, Request) -> Reply
call(ServerRef, Request, Timeout) -> Reply
  Types:
    ServerRef = Name | {Name,Node} | {global,GlobalName} | {via,Module,ViaName} | pid()
      Node = atom()
      GlobalName = ViaName = term()
    Request = term()
    Timeout = int()>0 | infinity
    Reply = term()

 

  通过发送请求向引用名称为ServerRef的gen_server实行共同调用,直到回复到达或产生超时。gen_server将调用Module:handle_call/三处理请求。

  那是子规范的类型定义:

  ServerRef可以是:

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()
  • 进程号;
  • Name,gen_server被地面注册的称号;
  • {Name,Node},gen_server在其余节点被本地注册;
  • {global,GlobalName},gen_server被全局注册;
  • {via,Module,ViaName},gen_server通过代表的长河注册表注册;

  
Id是三个称号,在内部被监督者用于标识子规范。

  Request是叁个任意term,它看成内部的参数字传送递给Module:handle_call/3。

  

  Timeout是2个超出零的平头,它钦命多少纳秒等待各类回复,原子infinity会Infiniti期的等待。暗中认可值是四千。假如在钦定时间内未有吸收回复,函数会调用失败。假使调用者捕获失败并且一而再运维,服务器仅仅晚些回复,它将在别的时候到达随后进入调用者的新闻队列。对此,调用者必须准备那种情状,并且不保留任何杂质消息,它们是五个成分元组作为第二个因素。

  
StartFunc定义函数调用用于运行子进程。它应当是3个模块-函数-参数元组{M,F,A}用作apply(M,F,A)。

  再次回到值Reply被定义在Module:handle_call/3的回来值里。

  
运行函数必须创制和链接到子进度,应该回到{ok,Child}或{ok,Child,Info},个中Child是子进程的进度号,Info是任意term被监督者忽略。

  调用只怕会因为三种原因失利,包含超时和在调用前和调用进度中gen_server死掉。

 

  在调用时期当连接到客户端,假诺服务器死掉,有时会消耗退出音讯,那些过时的行事已经在OTP
瑞鹰1贰B/Erlang 五.陆中移除。

  
假若实进度因为有个别缘故不可能运行,运营函数也足以回去ignore,那种处境,子规范将会被监督者保存(除非它是temporary进度),但不存在的子进度将被忽视。

multi_call(Name, Request) -> Result
multi_call(Nodes, Name, Request) -> Result
multi_call(Nodes, Name, Request, Timeout) -> Result
  Types:
    Nodes = [Node]
      Node = atom()
    Name = atom()
    Request = term()
    Timeout = int()>=0 | infinity
    Result = {Replies,BadNodes}
      Replies = [{Node,Reply}]
        Reply = term()
      BadNodes = [Node]

 

  对具备在钦定节点上被地面注册为Name的gen_server实行联合调用,通过第三次发送请求到每种节点,然后等待恢复生机。这几个gen_server进程将会调用Module:handle_call/三处理请求。

  
要是出现错误,函数也大概回到错误元组{error,Error}。

  函数重返元组{Replies,BadNodes},在那之中,Replies是{Node,Reply}的列表,BadNodes是不设有节点的列表,或gen_server
Name不存在或尚未过来。

 

  Nodes是伸手被发送到的节点名称列表。暗中同意值是中拥有已知的节点列表[node()|nodes()]。

  
请注意,start_link函数,不一样的行事模块满足上述的渴求。

  Name是每个gen_server被地面注册的称谓。

 

  Request是3个任意term,它当作在那之中的参数传递给Module:handle_call/3。

  
Restart定义什么日期甘休的子进度应该被重启。1个permanent子进度应该总是被重启,一个temporary子进度未有被重启(尽管当监督者的重启策略是rest_for_one或one_for_all和兄弟进度的挂掉导致temporary进程被终止),和三个transient子进程应该被重启仅当它丰富终止,也正是说,除过normal,shutdown或{shutdown,Term}的别样退出原因。

  Timeout是八个大于零的平头,它钦点多少皮秒等待各种回复,原子infinity会Infiniti期的守候。暗中认可值是infinity。假如在钦定时间内节点未有收受回复,该节点被投入到BadNodes。
当在节点Node来自gen_server回复Reply到达,{Node,Reply}被加入到Replies。Reply被定义在Module:handle_call/三的归来值里。

 

  为制止随后的回答(在逾期从此)污染调用者的音讯队列,三其中等人经过被用来加强在的调用。当它们到达四个停歇的历程,迟到的复原将不会被保留。

  
Shutdown定义子进度应该怎样被终止。brutal_kill意味着子进程将被白白停止使用exit(Child,kill)。1个整数超时值意味着监督者将告诉子进度经过调用exit(Child,shutdown)来终止,然后以原因shutdown等待来自子进程的脱离非确定性信号。如若在钦赐数量皮秒内尚未接到退出数字信号,子进程使用exit(Child,kill)被白白停止。

cast(ServerRef, Request) -> ok
  Types:
  ServerRef = Name | {Name,Node} | {global,GlobalName} | {via,Module,ViaName} | pid()
    Node = atom()
    GlobalName = ViaName = term()
  Request = term()

 

  发送1个异步请求到引用名ServerRef的gen_server,然后随即重回ok,若是目的节点或gen_server不设有,忽略音信。gen_server将调用Module:handle_cast/二处理请求。

  
假如实进度是另一个监督者,Shutdown应该被设置成infinity,给子树丰富的时日关闭。假诺实进度是劳力,它也被允许设置成infinity。

  参看call/2,3,了解ServerRef的描述。

 

  Request是2个任意term,它看成内部的参数字传送递给Module_cast/2。

  
警告:小心设置shutdown策略为infinity,当子进度是1个劳力。因为,在那种气象下,监察和控制树的终止取决于子进度,它必须以安全的办法完成,它的清理进程必须回到。

abcast(Name, Request) -> abcast
abcast(Nodes, Name, Request) -> abcast
  Types:
    Nodes = [Node]
      Node = atom()
    Name = atom()
    Request = term()

 

  发送2个异步请求给在钦赐节点被本地注册为Name的gen_server。函数即刻回到并且忽略不设有的节点,或gen_server
Name不存在。gen_server将调用Module:handle_cast/2处理请求。

  
请注意,全数子进度自动使用标准OTP行为模块实现基于关闭协议。

  参看 multi_call/二,三,肆,明白参数描述。

 

reply(Client, Reply) -> Result
  Types:
    Reply = term()
    Result = term()

  
Type钦命子进程是监督者依旧工笔者。

  当回复未有定义在Module:handle_call/3的回到值里,该函数能够被gen_server用来显式地发送过来给贰个调用
call/二,③ 或 multi_call/2,叁,4的客户端。

 

  Client必须是提必要回调函数的From参数。Reply是二个任意term,它将用作call/二,三或 multi_call/二,三,四的再次回到值被复苏到客户端。

  
Modules被版本处理程序使用,在代码替换时期用于分明什么过程使用什么模块。作为1个经历法则,Modules应该三个要素列表[Module],在那之中,Module是回调模块,假若子进度是1个supervisor,
gen_server 或
gen_fsm。借使实进度是二个饱含回调模块集合的轩然大波管理器(gen_event),Modules应该是dynamic。关于版本控制的越多音讯参考OTP设计基准。

  再次来到Result未有被进一步定义,并且应该总是被忽略。

 

enter_loop(Module, Options, State)
enter_loop(Module, Options, State, ServerName)
enter_loop(Module, Options, State, Timeout)
enter_loop(Module, Options, State, ServerName, Timeout)
  Types:
    Module = atom()
    Options = [Option]
      Option = {debug,Dbgs}
        Dbgs = [Dbg]
          Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}
    State = term()
    ServerName = {local,Name} | {global,GlobalName} | {via,Module,ViaName}
      Name = atom()
      GlobalName = ViaName = term()
    Timeout = int() | infinity

  
内部地,监督者也跟踪子进度的历程号,或undefined假使未有经过号存在。

  使1个已存在的进度进入贰个gen_server。不回来,反而这一个调用进度将进入gen_server的选择循环,并变为2个gen_server过程。那些历程必须接纳proc_lib的起步函数被运转。用户为该进程的其他开首化负责,包蕴为它注册2个名字。

 

  这几个函数卓殊有用,当供给贰个越来越错综复杂的初步化进度,而不是gen_server行为提供的。

数据类型

  Module,Option
和ServerName与调用gen_server:start[_link]/三,4全体同样的意思。可是,假如ServerName被钦点,进度必须在该函数被调用前相应地被注册。

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

  State和Timeout与Module:init/一的重回值有着同样的意思。回调模块Module也不必要导出3个init/一函数。

 

  失利:若是调用进度未被多少个proc_lib函数运转,或许只要它未根据ServerName注册。

导出

回调函数

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(Args) -> Result
  Types:
    Args = term()
    Result = {ok,State} | {ok,State,Timeout} | {ok,State,hibernate} | {stop,Reason} | ignore
      State = term()
      Timeout = int()>=0 | infinity
      Reason = term()

  创立四个监督者进度作为监察和控制树的壹部分,在别的地点,函数将保险监督者链接到调用者进度(它的监督者)。

  无论几时五个gen_server使用gen_server:start/3,4 或
gen_server:start_link/3,肆被运维,该函数被二个新进度调用去初始化。

 

  Args是提须求运营函数的参数。

  被创设的监督者进度调用Module:init/1找出重启策略、最大运转频率和子进度。为保证联合运维进度,start_link/二,叁不会重返直到Module:init/壹已经再次回到且全体子进度已被运营。

  借使开始化成功,函数应该回到{ok,State}, {ok,State,Timeout} 或
{ok,State,hibernate},在那之中,State是gen_server的个中意况。

 

  假诺2个平头超时值被提供,三个过期将发生,除非在提姆eout阿秒内收取一个呼吁或音讯。贰个逾期被timeout原子标识,它应有被handle_info/一次调函数处理。infinity能够被用来Infiniti期地等候,那是暗许值。

  假使SupName={local,Name},supervisor使用register/贰被注册为地面的Name。假使SupName={global,Name},supervisor使用global:register_name/二被登记为全局的Name。倘诺未有提供Name,supervisor不会被注册。假若SupName={via,Module,ViaName},supervisor将会用Module代表的注册表注册。Module回调应该导出函数register_name/2,
unregister_name/1, whereis_name/一 和
send/二,它们表现得像global模块对应的函数。由此,{via,global,Name}是三个可行地引用。

  假设hibernate被钦点而不是三个超时值,进度将进入休眠当等待下一条信息到达时(调用proc_lib:hibernate/3)。

 

  假诺在开首化时期出现错误,函数再次来到{stop,Reason},其中,Reason是别的term,或ignore。

  假诺未有提供名称,监督者不会登记。

Module:handle_call(Request, From, State) -> Result
  Types:
    Request = term()
    From = {pid(),Tag}
    State = term()
    Result = {reply,Reply,NewState} | {reply,Reply,NewState,Timeout} | {reply,Reply,NewState,hibernate}
      | {noreply,NewState} | {noreply,NewState,Timeout} | {noreply,NewState,hibernate}
      | {stop,Reason,Reply,NewState} | {stop,Reason,NewState}
      Reply = term()
      NewState = term()
      Timeout = int()>=0 | infinity
      Reason = term()

 

  无论几时使用gen_server:call/2,3 或
gen_server:multi_call/2,3,4,gen_server接收请求发送,该函数被调用处理请求。

  Module是回调模块的称谓。

  Request是提需要call或multi_call的参数。

 

  From是一个元组{Pid,Tag},当中,Pid是客户端的进度号,Tag是一个唯1标志。

  Args是三个任意term,作为参数字传送递给Module:init/一。

  State是gen_server的内部情状。

 

  假诺函数再次回到{reply,Reply,NewState}, {reply,Reply,NewState,Timeout}
或 {reply,Reply,NewState,hibernate},Reply将被还原给From作为call/二,叁 或
multi_call/二,三,四的重返值。然后gen_server继续执行,或许更新内部景色NewState。参看Module:init/一掌握Timeout和hibernate的叙述。

  假若监督者和它的子进度被成功成立,(相当于说,如若全部子进度运行函数重临{ok,Child},
{ok,Child,Info}, 或
ignore)函数再次回到{ok,Pid},个中Pid是监督者的进度号。假设已存在内定SupName的历程,函数重回{error,{already_started,Pid}},个中,Pid是这个进度的进度号。

  如若函数再次来到{noreply,NewState}, {noreply,NewState,Timeout} 或
{noreply,NewState,hibernate},gen_server将用NewState继续执行。任何对From的上升必须显式使用gen_server:reply/2。

 

  假设函数重返{stop,Reason,Reply,NewState},Reply将还原给From。假使函数重回{stop,Reason,NewState},任何对From的复原必须显式使用gen_server:reply/贰。然后,函数将调用Module:terminate(Reason,NewState),随后停下。

  假若Module:init/1重临ignore,该函数也回到ignore,而监督者以原因normal终止。固然Module:init/壹失利或再次回到不得法的值,该函数再次回到{error,Term},在这之中,Term是含有关于错误音信的Term,监督者以原因Term终止。

Module:handle_cast(Request, State) -> Result
  Types:
    Request = term()
    State = term()
    Result = {noreply,NewState} | {noreply,NewState,Timeout} | {noreply,NewState,hibernate} | {stop,Reason,NewState}
      NewState = term()
      Timeout = int()>=0 | infinity
      Reason = term()

 

  无论什么日期,gen_server接收多个伸手发送使用gen_server:cast/2 或
gen_server:abcast/二,3,该函数被调用处理请求。

  若是任何子进度运营函数是退步或重临3个错误元组或3个不当班值日,监督者首先将以原因shutdown终止全部已开发银行的进度,随后停下它和谐,然后再次回到{error,
{shutdown, Reason}}。

  参见Module:handle_call/3领会参数和恐怕再次回到值的叙述。

 

Module:handle_info(Info, State) -> Result
  Types:
    Info = timeout | term()
    State = term()
    Result = {noreply,NewState} | {noreply,NewState,Timeout} | {noreply,NewState,hibernate} | {stop,Reason,NewState}
      NewState = term()
      Timeout = int()>=0 | infinity
      Reason = normal | term()
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()

  该函数被gen_server调用,当超时发出或接收到其余新闻而不是联合或异步请求(或然系统音讯)。

  动态增添一个子规范到监督者SuperRef,它运维相应的子进度。

  Info是原子timeout,当超时爆发,或是已吸收的新闻。

  ServerRef可以是:

Module:terminate(Reason, State)
  Types:
    Reason = normal | shutdown | {shutdown,term()} | term()
    State = term()    

– 进程号;

Name,supervisor被本地注册的名称;
  • {Name,Node},supervisor在其余节点被本地注册;
  • {global,Name},supervisor被全局注册;
  • {via,Module,ViaName},supervisor通过代表的进度注册表注册。

 

  ChildSpec应该是行得通的子进度(除非该监督者是一个simple_one_for_one的监督者,看上边)。子进度将会选用定义在子规范的开发银行函数运转。

 

  如果是simple_one_for_one监督者的景色下,定义在Module:init/一的子规范将被采取,ChildSpec应该是3个任意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}。

  如若实进度运转哈数重临3个破绽百出元组或1个错误值,
或它失败,子规范被扬弃,函数重返{error,Error},当中Error是3个涵盖关于错误和子规范音信的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/贰移除子规范。

 

  借使实进度是temporary,进度1终止,子规范就被删去。那意味delete_child/2未有意义,restart_child/二无法用于那些进程。

 

  要是监督者是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}。

  子进度运行函数再次来到多个不当元组或2个荒唐值,或它败北,函数再次回到{error,Error},其中,Error是3个包括错误音信的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}

  再次来到2个属性列表,它涵盖监督者子规范的下列成分和被管制的进度的数码:

  •      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监督者被运转,函数被3个新的长河调用找出重启策略、最大重启频率和子规范。

 

  Args是提供给运转函数的参数。

 

  RestartStrategy是重启策略,马克斯安德拉 和
马克斯T定义监督者的最大重启频率。
[ChildSpec]是一组有效地子规范,它定义哪些进度监督者应该运维和监理。参看下面境海关于监督规则的议论。

 

  请留心,当重启策略为simple_one_for_one,子规范列表必须只含有四个子行业内部列表(ID被忽视)。在开始化时期,未有子进程随后被运营,不过全数子进程被设定使用supervisor:start_child/贰来动态运营。

 

  函数也可能回到ignore。

 

  

  翻译有题目标地方,请大家指正。

  该函数被gen_server调用,当它准备甘休。它应有和Module:init/壹相反,并做供给的清理。当它回到时,gen_server由于Reason终止。重临值被忽略。

  Reason是二个term,提议结束原因,State是gen_server的内部景色。

  Reason取决于gen_server终止的原由。借使因为另叁个回调函数已经重返三个甘休元组{stop,..},Reason将会有钦命的值在那贰个元组。假使是出于退步,Reason是错误原因。

  如果gen_server是监察和控制树的一局地,并且被监察和控制者有序终止,该函数将被调用,使用Reason=shutdown,假若应用以下情况:

  • gen_server已经设置为脱离连续信号;
  • 并且,被定义在监察和控制者的子规范的关闭策略是3个平头值,而不是brutal_kill。

  即使gen_server不是监察和控制者的一片段,假诺接受来自父进度的’EXIT’音信,函数将被调用。Reason将和’EXIT’新闻未有差距于。

  否则,gen_server将随即甘休。

  注意,除了normal,shutdown,或{shutdown,Term}的其余原因,gen_server被设定终止由于3个荒唐,并且选择error_logger:format/二报告三个张冠李戴。

Module:code_change(OldVsn, State, Extra) -> {ok, NewState} | {error, Reason}
  Types:
    OldVsn = Vsn | {down, Vsn}
      Vsn = term()
    State = NewState = term()
    Extra = term()
    Reason = term()

  该函数被gen_server调用,当它在本子升级/降级应该更新本人的内部景况,相当于说,当指令{update,Module,Change,…}在appup文件中被交付,个中Change={advanced,Extra}。参看OTP设计基准查看越多新闻。

  在升级的图景下,OldVsn正是Vsn;在贬低的意况下,OldVsn正是{down,Vsn}。Vsn被回调模块Module的老版本的vsn属性定义。如若未有这么的质量定义,版本就是BEAM文件的校验和。

  State是gen_server的中间景观。

  Extra来自升级指令的{advanced,Extra}部分,被形容传递。

  借使成功,函数应该回到被更新的在那之中情况。

  假若函数再次来到{error,Reason},正在开展的提高将会破产,并且回滚到老版本。

Module:format_status(Opt, [PDict, State]) -> Status
  Types:
    Opt = normal | terminate
    PDict = [{Key, Value}]
    State = term()
    Status = term()

  请留意,该回调可选,所以回调模块不需求导出它,那么些回调模块提供3个暗许落成,该函数重回回调模块状态。

  该函数被gen_server进程调用:

  • sys:get_status/壹,二被调用获取gen_server状态。那种情景,Opt被设置成normal。
  • gem_server至极终止,生成错误日志。这种景观,Opt被安装成terminate。

  该函数是有效的,对于这几个情状定制gen_server的格式和展现。一个回调模块希望定制sys:get_status/1,贰的重回值,和它在甘休错误日志的景观呈现,导出三个format_status/贰实例,重返描述gen_server当前事态的term。

  PDict是gen_server的长河字典的日前值。

  State是gen_server的中间景色。

  函数应该回到Status,定制当前景色的细节和gen_server的景观的term。在Status格式上尚无别的限制,不过对于sys:get_status/壹,二场馆,对于Status建议的格式是[{data,
[{“State”,
Term}]}],其中,Term提供gen_server相关的细节。遵从那么些提出不是必须的,可是这么做将使回调模块的状态与sys:get_status/1,二的再次来到值1致。

  该函数的一个用法是重回紧密的轮换状态表示来制止有过大的图景项打印在日记里。

相关文章