erlang中文手册——gen_server

时间:2022-06-01 21:05:08

声明:本文章是对erlang手册的部分内容的中文翻译,版权归原作者所有……由于本人的英语水平有限(翻译此手册的目的之一就是提高英语水平^0^),所以翻译有误乃正常现象,如有发现请及时提醒,THX……

谨以此文献给初学erlang并且不喜欢看英文手册的朋友……
 

  gen_server
模块
        gen_server


模块摘要

      通用服务器行为


描述

       为c/s结构实现服务器的行为模块。用该模块实现的通用服务器进程(gen_server)有标准的函数接口集,并且有追踪和错误报告的功能。它还能纳入OTP的监控树.更多的信息,请参阅OTP设计原理。
gen_server假定所有的细节部分都实现在导出了一系列预定义函数集的回调模块中。行为函数和回调函数的对应关系如下:
gen_server module            Callback module
-----------------                            ---------------
gen_server:start_link -----> Module:init/1

gen_server:call
gen_server:multi_call -----> Module:handle_call/3

gen_server:cast
gen_server:abcast     -----> Module:handle_cast/2

-                                 -----> Module:handle_info/2

-                                  -----> Module:terminate/2

-                                   -----> Module:code_change/3    
如果一个回调函数失败或返回错误的值,gen_server进程会终止。gen_server像sys(3)中描术的那样处理系统消息,sys模块可以用来调试gen_server进程。
注意,gen_server并不会自动捕获exit信号,这需要在回调模块中明确地设定。

除非特别说明,该模块的所有函数在指定的gen_server不存在或输入的参数不正确时都会失败。如果回调函数明确地指定’hibernate’替代其他超时值,gen_server进程将进入睡眠状态(见erlang(3)).这在服务器需要长时间空闲时很有用。然而,需要慎用这个特性,因为睡眠意味着需要两次垃圾回收(睡眠时和唤醒后短时间里),并且,并非每次对繁忙服务器的调用都是你想要的结果。


导出


start_link(Module, Args, Options) -> Result
start_link(ServerName, Module, Args, Options) -> Result

    Types:
        ServerName = {local,Name} | {global,GlobalName}
         Name = atom()
         GlobalName = 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()
创建一个可以纳入监控树的gen_server进程。函数必须直接或间接得被监控者调用,只有这样才能确保gen_server链接到监控者进程。gen_server进程调用Module:init/1进行初始化.为了确保启动过程同步,直到Module:init/1返回时,start_link/3,4才返回。如果ServerName={local, Name},将调用register/2 在本地把gen_server注册为Name.如果ServerName={global, GlobalName}, 将调用global:register_name/2把gen_server注册为名字叫GlobalName的全局进程。如果没有提供名字,则不注册.
Module 是回调模块的名字.
Args 是传给Module:init/1 的参数.

如果指定了选项{timeout,Time},gen_server必须在Time毫秒内初始化完毕,否则进程会终止且开始函数返回{error,timeout}.
如果指定了选项{debug,Dbgs},对于Dbgs的每一项,相应的sys函数都会被调用.见sys(3).
如果指定了选项{spawn_opt,SOpts}, SOpts将会作为选项列表传给启动gen_server的内建函数spawn_opt. 见 erlang(3).
注意:
目前还不支持使用spawn选项monitor,并且会使函数以badarg原因而失败。
如果gen_server成功创建并初始化,函数将返回{ok,Pid},其中Pid是gen_server的进程ID.如果已存在一个指定为ServerName的进程,函数将返回{error, {already_started,Pid}},其中Pid是已存在进程的进程ID.
如果Module:init/1因Reason原因失败,该函数返回{error, Reason}.
如果Module:init/1返回{stop,Reason}或ignore,进程将终止,并分别返回{error,Reason}或ignore.


start(Module, Args, Options) -> Result

start(ServerName, Module, Args, Options) -> Result
    Types:
        ServerName = {local,Name} | {global,GlobalName}
         Name = atom()
         GlobalName = 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()

创建一个独立的gen_server 进程,也就是说这个gen_server不能纳入监控树,因此也没有监控者。
关于参数和返回值请参考start_link/3,4.


call(ServerRef, Request) -> Reply

call(ServerRef, Request, Timeout) -> Reply
    Types:
        ServerRef = Name | {Name,Node} | {global,GlobalName} | pid()
                 Node = atom()
                 GlobalName = term()
        Request = term()
        Timeout = int()>0 | infinity
        Reply = term()
以同步调用的方式向gen_server发送请求,并等待一个返回或发生超时。gen_server将调用Module:handle_call/3来处理该请求。
其中ServerRef 可以是:
•    进程ID,
•    进程名:如果gen_server被注册为本地进程的话;
•    {Name,Node}:如果gen_server在其它结点注册的话;
•    {global,GlobalName}:如果gen_server注册为全局进程的话.


Request 可以为任意项,其作为参数之一传递给Module:handle_call/3。

Timeout为大于0的整数,指定了等待返回值的最长时间,或者设为infinity让函数无限等待。默认值为5000(毫秒).如果在指定的时间内没有收到返回值,函数调用将失败。如果调用者捕获了失败信息并继续运行,而且gen_server过后有返回值,该值将放入调用者的消息队列中,这种情况下,调用者必须做一些处理,并且丢弃这种含有两元素第一个元素为reference的元组的垃圾信息.
返回值Reply是Module:handle_call/3中定义的返回值。
该调用可以因多种原因而失败,包括超时、调用前或调用时gen_server正在终止.


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/3来处理请求。


函数返回一个元组{Replies,BadNodes},其中Replies是以{Node,Reply}为元素的列表,BadNodes是以下情况的节点列表:节点不存在,或以Name为名的gen_server在该节点不存在或无返回。

Nodes是发送请求的目标节点名的列表,默认值是所有已知节点的列表[node()|nodes()].
Name是每个gen_server在其本地注册的名字。
Request 可以为任意项,其作为参数之一传递给Module:handle_call/3。
Timeout为大于0的整数,指定了等待返回值的最长毫秒数,或者设为infinity让函数无限等待。默认值为infinity.如果在指定的时间内没有收到节点的返回,该节点将加到BadNodes中。当从一个节点Node的gen_server收到返回值Reply时,{Node,Reply}将被加到Replies中。Reply是在Module:handle_call/3中定义的返回值.


警告:

    如果其中有节点没有进程监控能力,比如C或Java节点,并且发送请求时gen_server没有启动,但在2秒内启动了,该函数将等待整个超时时间(Timeout),
甚至无限等待。当所有节点都是Erlang节点时该问题不存在。

为了避免延时返回(超时后到达的返回)污染调用者的消息队列,有一个中间进程来做实际的调用。当延时返回发送结已终止进程时将被丢弃。


cast(ServerRef, Request) -> ok
    Types:
        ServerRef = Name | {Name,Node} | {global,GlobalName} | pid()
                 Node = atom()
                 GlobalName = term()
        Request = term()
向gen_server ServerRef发向导步请求,并立即返回,不管目标节点或gen_server是否存在。gen_server将调用Module:handle_cast/2来处理请求。见call/2,3对ServerRef的描述。
Request可以是任意数据项,并作为传给Module:handle_cast/2的参数之一。


abcast(Name, Request) -> abcast
abcast(Nodes, Name, Request) -> abcast

     Types:
            Nodes = [Node]
                  Node = atom()
            Name = atom()
            Request = term()

向指定的节点中所有注册为Name的gen_server发送异步请求。不管节点和gen_server Name是否存在,函数都立即返回.gen_server将调用Module:handle_cast/2来处理请求。
见multi_call/2,3,4对参数的描述。


reply(Client, Reply) -> Result
    Types:
        Client - see below
        Reply = term()
        Result = term()
当返回值不能在Module:handle_call/3的返回值中定义时,gen_server可以用该函数明确地给调用call/2,3或multi_call/2,3,4的调用者Client发送返回值.
Client必须是传递给回调函数的参数From.Reply可以是任意数据项,并作为call/2,3或mulit_call/2,3,4的返回值返回给调用者Client.
返回值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}
                 Name = atom()
                 GlobalName = term()
        Timeout = int() | infinity
使一个已存在的进程成为gen_server.函数不会返回,调用该函数的进程将进入gen_server的接收循环,并成为一个gen_server进程。调用进程必须是用proc_lib中的启动函数之一启动的。使用者需要负责对进程做所有初始化工作,包括给进程注册名字。当初始化过程非常复杂,而gen_server行为提供的初始化接口不能完成时,这个函数非就常有用。
Module,Options 和ServerName跟调用gen_server:start[_link]/3,4时的意义一样。但是,如果指定了ServerName的话,调用进程必须在调用该函数前已经被注册为ServerName。
State 和Timeout跟Module:init/1返回值中的意义一样。并且回调模块不必导出init/1函数。
失败情况:调用进程不是通过proc_lib中的启动函数启动的,或者没用注册为ServerName.


回调函数
          下列函数必须从gen_server回调模块中导出。

EXPORTS

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,4启动时,新启动的进程都会调用该函数进行初始化。
Args是传给启动函数的参数中的Args.
如果初始化成功,函数应该返回{ok,State},{ok,State,Timeout}或{ok,State,hibernate},其中State是gen_server的内部状态。
如果指定了整型超时值,在Timeout毫秒内没有收到请求消息就发生超时。超时信息用原子timeout标识并且由handle_info/2回调函数处理。原子infinity是默认值,用来表示无限等待。
如果用原子hibernate作为超时值,在等待下个消息到达时进程将进入睡眠状态(通过调用proc_lib:hibernate/3).
如果在初始化的过程中发生错误,函数应该返回{stop,Reason},其中Reason可以是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收到通过调用gen_server:call/2,3或gen_server:multi_call/2,3,4发送的请求时,都会调用该函数来处理请求。
Request是传递给call或multi_call的参数Request.
From是元组{Pid,Tag},其中Pid是发送请求的进程ID,Tag是一个唯一标签。
State是gen_server的内部状态。
如果函数返回{reply, Reply,NewState},{reply, Reply,NewState, Timeout}或{reply, Reply,NewState, hibernate}, Reply将作为call/2,3的返回值或mulit_call/2,3,4的返回值的一部分返回给From.然后gen_server用可能更新过的内部状态NewState继续运行。见Module:init/1对Timeout和hibernate的描述。
如果函数返回{noreply, NewState},{noreply, NewState,Timeout}或{noreply,NewState,hibernate},gen_server将用NewState作为内部状态继续运行。给From返回值必须明确地调用gen_server:reply/2.
如果函数返回{stop, Reason,Reply, NewSate},Reply将返回给From.如果函数返回{stop,Reason,NewState},给From返回值必须明确地调用gen_server:reply/2.然后gen_server将调用Module:terminate(Reason,NewState)终止。

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,3或gen_server:abcast/2,3,4发送的请求时,都会调用该函数来处理请求。
见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()
当发生超时或收到其他同步和异步请求之外的消息(如通过!发送的消息或系统消息)时,gen_server将调用该函数处理请求。
Info是原子timeout(当发生超时时)或接收到的消息。
见Module:handle_call/3对其它参数和返回值的描述。

Module:terminate(Reason, State)
    Types:
        Reason = normal | shutdown | {shutdown,term()} | term()
        State = term()
当gen_server要终止时将调用该函数。该函数应与Module:init/1相反,做一些必要的清理工作。当函数返回后,gen_server以Reason为原因而终止。返回值被忽略。
Reason是表示终止原因的数据项,State是gen_server的内部状态。
Reason取决于gen_server为何而终止。如果是因为其他的回调函数返回终止元组{stop,..},Reason就是元组中指定的值。如果是因为某个失败而终止,Reason是发生错误的原因。
如果gen_server在监控树中,并被其监控者终止,在以下情况下,该函数以Reason=shutdown被调用:gen_server被设置为捕获exit信号并且监控者的子树描述中终止策略是一个整型超时值,而不是brutal_kill.
即使gen_server不在监控树中,如果从父进程中收到’EXIT’消息,该函数也会被调用。Reason跟’EXIT’消息中Reason的一样。
其他情况下,gen_server将立即终止。


除了normal,shutdown或{shutdown,Term}外的其他原因,gen_server将被认为是因为发生错误而终止,并且用error_logger:format/2产生一个错误报告。


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进行版本升级/降级过程中,需要更新内部状态时(也就是说,当appup文件中指定了{update, Module,Change,...},其中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()
该回调函数是可选的,所以回调模块可以不导出该函数。gen_server模块提供了该函数的一个默认实现,并返回回调模块的状态。
在下列情况下,gen_server进程将调用该函数:
    .调用sys:get_status/1,2获取gen_server的状态(这种情况下,Opt被设为原子normal);
    .gen_server非正常终止并记录了一个错误(这种情况下,Opt被设置为原子terminate).
在以下情况中,该函数对于定制gen_server的状态的表现形式非常有用:回调模块想要定制sys:get_status/1,2的返回值和它的状态在终止错误记录输出的format_status/2返回的gen_server当前状态描述数据项中的表现形式。
PDict是gen_server进程字典的当前值。
State是gen_server的内部状态。


函数返回的Status是对gen_server当前状态详细信息的定制。对于Status形并没有限制,但对于sys:get_status/1,2(当Opt为normal时),Status的值的形式建议为[{data,[{“State”,Term}]}],其中Term提供了gen_server状态的相关细节。该建议不一定要遵循,但这样做可以使回调模块的状态与sys:get_status/1,2的其他返回值相一致。

该函数的其中一个用处是返回压缩形式的状态描述,以免在日志文件中打印过大的状态数据。