Erlang模块gen_server翻译

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。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()

  创建一个gen_server进程作为监控树的一部分。函数应该直接或间接地被supervisor调用。它将确保gen_server在其它方面被链接到supervisor。

  gen_server进程调用Module:init/1进行初始化。为确保同步启动过程,start_link/3,4直到Module:init/1执行完成才能返回。

  如果ServerName={local,Name},gen_server使用register/2被注册为本地的Name。如果ServerName={global,GlobalName},gen_server使用global:register_name/2被注册为全局的GlobalName。如果没有提供Name,gen_server不会被注册。如果ServerName={via,Module,ViaName},gen_server将会用Module代表的注册表注册。Module回调应该导出函数register_name/2, unregister_name/1, whereis_name/1 和 send/2,它们表现得像global模块对应的函数。因此,{via,global,GlobalName}是一个有效地引用。

  Module是回调模块的名称。

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

  如果选项是{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是那个进程的进程号。

  如果Module:init因为Reason失败,函数返回{error,Reason}。如果Module:init/1返回{stop,Reason} 或 ignore,进程被终止并且函数会分别返回{error,Reason} 或 ignore。

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

  创建一个独立的gen_server进程,也就是,gen_server不是监控树的一部分并且没有监控进程。

  参看start_link/3,4了解参数和返回值的描述。

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

  ServerRef可以是:

  • 进程号;
  • Name,gen_server被本地注册的名称;
  • {Name,Node},gen_server在其它节点被本地注册;
  • {global,GlobalName},gen_server被全局注册;
  • {via,Module,ViaName},gen_server通过替代的进程注册表注册;

  Request是一个任意term,它作为其中的参数传递给Module:handle_call/3。

  Timeout是一个大于零的整数,它指定多少毫秒等待每个回复,原子infinity会无限期的等待。默认值是5000。如果在指定时间内没有收到回复,函数会调用失败。如果调用者捕获失败并且继续运行,服务器仅仅晚些回复,它将在任何时候到达随后进入调用者的消息队列。对此,调用者必须准备这种情况,并且不保存任何垃圾消息,它们是两个元素元组作为第一个元素。

  返回值Reply被定义在Module:handle_call/3的返回值里。

  调用可能会因为几种原因失败,包括超时和在调用前和调用过程中gen_server死掉。

  在调用期间当连接到客户端,如果服务器死掉,有时会消耗退出消息,这个过时的行为已经在OTP R12B/Erlang 5.6中移除。

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是不存在节点的列表,或gen_server Name不存在或没有回复。

  Nodes是请求被发送到的节点名称列表。默认值是中所有已知的节点列表[node()|nodes()]。

  Name是每个gen_server被本地注册的名称。

  Request是一个任意term,它作为其中的参数传递给Module:handle_call/3。

  Timeout是一个大于零的整数,它指定多少毫秒等待每个回复,原子infinity会无限期的等待。默认值是infinity。如果在指定时间内节点没有收到回复,该节点被加入到BadNodes。
当在节点Node来自gen_server回复Reply到达,{Node,Reply}被加入到Replies。Reply被定义在Module:handle_call/3的返回值里。

  为避免随后的应答(在超时之后)污染调用者的消息队列,一个中间人进程被用来做实际的调用。当它们到达一个终止的进程,迟到的回复将不会被保存。

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

  发送一个异步请求到引用名ServerRef的gen_server,然后立即返回ok,如果目标节点或gen_server不存在,忽略消息。gen_server将调用Module:handle_cast/2处理请求。

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

  Request是一个任意term,它作为其中的参数传递给Module_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:
    Reply = term()
    Result = term()

  当回复没有定义在Module:handle_call/3的返回值里,该函数可以被gen_server用来显式地发送回复给一个调用 call/2,3 或 multi_call/2,3,4的客户端。

  Client必须是提供给回调函数的From参数。Reply是一个任意term,它将作为call/2,3 或 multi_call/2,3,4的返回值被回复到客户端。

  返回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

  使一个已存在的进程进入一个gen_server。不返回,反而这个调用进程将进入gen_server的接收循环,并成为一个gen_server进程。这个进程必须使用proc_lib的启动函数被启动。用户为该进程的任何初始化负责,包括为它注册一个名字。

  这个函数非常有用,当需要一个更加复杂的初始化过程,而不是gen_server行为提供的。

  Module,Option 和ServerName与调用gen_server:start[_link]/3,4有着相同的含义。然而,如果ServerName被指定,进程必须在该函数被调用前相应地被注册。

  State和Timeout与Module:init/1的返回值有着相同的含义。回调模块Module也不需要导出一个init/1函数。

  失败:如果调用进程未被一个proc_lib函数启动,或者如果它未依据ServerName注册。

回调函数

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是提供给启动函数的参数。

  如果初始化成功,函数应该返回{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是任何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接收请求发送,该函数被调用处理请求。


  Request是提供给call或multi_call的参数。

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

  State是gen_server的内部状态。

  如果函数返回{reply,Reply,NewState}, {reply,Reply,NewState,Timeout} 或 {reply,Reply,NewState,hibernate},Reply将被回复给From作为call/2,3 或 multi_call/2,3,4的返回值。然后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,NewState},Reply将回复给From。如果函数返回{stop,Reason,NewState},任何对From的回复必须显式使用gen_server:reply/2。然后,函数将调用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 或 gen_server:abcast/2,3,该函数被调用处理请求。

  参见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:terminate(Reason, State)
  Types:
    Reason = normal | shutdown | {shutdown,term()} | term()
    State = term()    

  该函数被gen_server调用,当它准备终止。它应该和Module:init/1相反,并做必要的清理。当它返回时,gen_server由于Reason终止。返回值被忽略。

  Reason是一个term,指出停止原因,State是gen_server的内部状态。

  Reason取决于gen_server终止的原因。如果因为另一个回调函数已经返回一个停止元组{stop,..},Reason将会有指定的值在那个元组。如果是由于失败,Reason是错误原因。

  如果gen_server是监控树的一部分,并且被监控者有序终止,该函数将被调用,使用Reason=shutdown,如果应用以下状态:

  • gen_server已经设置为退出信号;
  • 并且,被定义在监控者的子规范的关闭策略是一个整数值,而不是brutal_kill。

  即使gen_server不是监控者的一部分,如果收到来自父进程的'EXIT'消息,函数将被调用。Reason将和'EXIT'消息一样。

  否则,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调用,当它在版本升级/降级应该更新自己的内部状态,也就是说,当指令{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()

  请注意,该回调可选,所以回调模块不需要导出它,这个回调模块提供一个默认实现,该函数返回回调模块状态。

  该函数被gen_server进程调用:

  • sys:get_status/1,2被调用获取gen_server状态。这种情况,Opt被设置成normal。
  • gem_server异常终止,生成错误日志。这种情况,Opt被设置成terminate。


  该函数是有用的,对于这些情况定制gen_server的格式和表现。一个回调模块希望定制sys:get_status/1,2的返回值,和它在终止错误日志的状态表现,导出一个format_status/2实例,返回描述gen_server当前状态的term。

  PDict是gen_server的进程字典的当前值。

  State是gen_server的内部状态。

  函数应该返回Status,定制当前状态的细节和gen_server的状态的term。在Status格式上没有任何限制,但是对于sys:get_status/1,2情况,对于Status建议的格式是[{data, [{"State", Term}]}],其中,Term提供gen_server相关的细节。遵循这些建议不是必须的,但是这样做将使回调模块的状态与sys:get_status/1,2的返回值一致。

  该函数的一个用法是返回紧凑的替换状态表示来避免有过大的状态项打印在日志里。

原文地址:https://www.cnblogs.com/sunbin-hello/p/6560980.html