查看源码 gen_server 行为 (stdlib v6.2)
通用服务器行为。
此行为模块在客户端-服务器关系中提供服务器。使用此模块实现的通用服务器进程(gen_server
)具有一组标准的接口函数,并包含跟踪和错误报告的功能。它也适用于 OTP 监督树。更多信息请参见 OTP 设计原则中的gen_server 行为部分。
gen_server
进程假设所有特定部分都位于导出预定义函数集的回调模块中。行为函数和回调函数之间的关系如下:
gen_server module Callback module
----------------- ---------------
gen_server:start
gen_server:start_monitor
gen_server:start_link -----> Module:init/1
gen_server:stop -----> Module:terminate/2
gen_server:call
gen_server:send_request
gen_server:multi_call -----> Module:handle_call/3
gen_server:cast
gen_server:abcast -----> Module:handle_cast/2
- -----> Module:handle_info/2
- -----> Module:handle_continue/2
- -----> Module:terminate/2
- -----> Module:code_change/3
如果回调函数失败或返回错误值,则 gen_server
进程将终止。但是,throw
类的异常不被视为错误,而是所有回调函数的有效返回值。
gen_server
进程按照 sys
中的描述处理系统消息。 sys
模块可用于调试 gen_server
进程。
请注意,gen_server
进程不会自动捕获退出信号,必须在回调模块中显式初始化。
除非另有说明,否则如果指定的 gen_server
进程不存在或指定了错误的参数,则此模块中的所有函数都将失败。
如果回调函数指定 'hibernate'
而不是超时值,则 gen_server
进程可以进入休眠状态(参见 erlang:hibernate/3
)。如果服务器预计长时间处于空闲状态,这将非常有用。但是,请谨慎使用此功能,因为休眠意味着至少进行两次垃圾回收(休眠时和唤醒后不久),并且不希望在每次调用繁忙的服务器之间都这样做。
如果 gen_server
进程需要在初始化后执行操作或将回调的执行分解为多个步骤,则它可以返回 {continue, Continue}
来代替超时值或休眠值,这将在接收任何外部消息/请求之前调用 Module:handle_continue/2
回调。
如果 gen_server
进程终止,例如,由于回调模块中的函数返回 {stop,Reason,NewState}
,则会向链接的进程和端口发送带有此 Reason
的退出信号。 有关使用退出信号进行错误处理的详细信息,请参见参考手册中的 进程。
注意
有关分布式信号的一些重要信息,请参见《Erlang 参考手册》进程章节中的通过分布式阻塞信号部分。阻塞信号可能会导致
gen_server
中的调用超时被显着延迟。
另请参阅
概要
类型
用于 start
或 enter_loop
函数的服务器启动选项。
描述 gen_server
状态的映射。
调用的回复目标。
将回复与相应请求关联的句柄。
不透明的请求标识符。 有关详细信息,请参见 send_request/2
。
不透明的请求标识符集合(request_id/0
)。
异步调用的响应超时。
服务器名称规范:local
、global
或通过 via
注册。
服务器规范:pid/0
或注册的 server_name/0
。
来自 start_monitor/3,4
函数的返回值。
用于 start
函数的服务器启动选项。
来自 start/3,4
和 start_link/3,4
函数的返回值。
函数
将请求投射到多个节点。
将请求投射到多个节点。
调用服务器:发送请求并等待响应。
将请求投射到服务器。
检查收到的消息是否为请求响应。
检查收到的消息是否为集合中的请求响应。
使调用进程成为 gen_server
进程。
使调用进程成为 gen_server
进程。
并行调用多个节点上的服务器。
并行调用多个节点上的服务器。
接收请求响应。
向客户端发送回复。
在集合中存储请求标识符。
创建空的请求标识符集合。
返回 ReqIdCollection
中的请求标识符的数量。
将请求标识符集合转换为列表。
发送异步 call
请求。
发送异步 call
请求,并将其添加到请求标识符集合。
启动服务器,既不链接也不注册。
启动服务器,既不链接也不注册。
启动服务器,已链接但未注册。
启动服务器,已链接但未注册。
启动服务器,已监视但既不链接也不注册。
启动服务器,已监视和注册,但未链接。
等待请求响应。
等待集合中的任何请求响应。
类型
-type enter_loop_opt() :: {hibernate_after, HibernateAfterTimeout :: timeout()} | {debug, Dbgs :: [sys:debug_option()]}.
用于 start
或 enter_loop
函数的服务器启动选项。
通过 enter_loop/3-5
或启动函数(如 start_link/3,4
)启动 gen_server
服务器时可以使用的选项。
{hibernate_after, HibernateAfterTimeout}
- 指定gen_server
进程等待任何消息HibernateAfterTimeout
毫秒,如果未收到消息,则进程会自动进入休眠状态(通过调用proc_lib:hibernate/3
)。{debug, Dbgs}
- 对于Dbgs
中的每个条目,将调用sys
中的相应函数。
-type format_status() :: #{state => term(), message => term(), reason => term(), log => [sys:system_event()]}.
描述 gen_server
状态的映射。
键是:
state
-gen_server
进程的内部状态。message
- 导致服务器终止的消息。reason
- 导致服务器终止的原因。log
- 服务器的 sys 日志。
新关联可能会在没有事先通知的情况下添加到状态映射中。
调用的回复目标。
Destination
,作为回调函数 Module:handle_call/3
的第一个参数传递给 gen_server
,用于通过 reply/2
(而不是通过回调函数的返回值)回复给使用 call/2,3
调用 gen_server
的进程 Client
。Tag
是对于此调用/请求实例唯一的项。
-opaque reply_tag()
将回复与相应请求关联的句柄。
-opaque request_id()
不透明的请求标识符。 有关详细信息,请参见 send_request/2
。
-opaque request_id_collection()
不透明的请求标识符集合(request_id/0
)。
每个请求标识符都可以与用户选择的标签关联。有关更多信息,请参阅 reqids_new/0
。
异步调用的响应超时。
用于设置使用 receive_response/2
、receive_response/3
、wait_response/2
或 wait_response/3
等待响应的时间限制。使用的时间单位为 毫秒
。
当前有效值
0..4294967295
- 相对于当前时间的超时,单位为毫秒。infinity
- 无限超时。也就是说,操作永远不会超时。{abs, Timeout}
- 绝对的 Erlang 单调时间超时,单位为毫秒。也就是说,当erlang:monotonic_time(millisecond)
返回的值大于或等于Timeout
时,操作将超时。Timeout
不允许指定比4294967295
毫秒更远的未来时间。当您有一个与完整请求集合 (request_id_collection/0
) 相对应的响应截止时间时,使用绝对值指定超时尤其方便,因为您不必反复重新计算到截止时间的相对时间。
-type server_name() :: {local, LocalName :: atom()} | {global, GlobalName :: term()} | {via, RegMod :: module(), ViaName :: term()}.
服务器名称规范:local
、global
或通过 via
注册。
在启动 gen_server
时使用。请参阅函数 start/3,4
、start_link/3,4
、start_monitor/3,4
、enter_loop/3,4,5
和类型 server_ref/0
。
{local, LocalName}
- 使用register/2
将gen_server
在本地注册为LocalName
。{global, GlobalName}
- 使用global:register_name/2
将gen_server
进程 ID 全局注册为GlobalName
。{via, RegMod, ViaName}
- 将gen_server
进程注册到由RegMod
表示的注册表。RegMod
回调应导出函数register_name/2
、unregister_name/1
、whereis_name/1
和send/2
,这些函数的行为应与global
中的相应函数相同。因此,{via, global, GlobalName}
是与{global, GlobalName}
等效的有效引用。
-type server_ref() :: pid() | (LocalName :: atom()) | {Name :: atom(), Node :: atom()} | {global, GlobalName :: term()} | {via, RegMod :: module(), ViaName :: term()}.
服务器规范:pid/0
或注册的 server_name/0
。
在寻址 gen_server
时使用。请参阅 call/2,3
、cast/2
、send_request/2
、check_response/2
、wait_response/2
、stop/2,3
和类型 server_name/0
。
可以是
pid/0
-gen_server
的进程标识符。LocalName
-gen_server
使用register/2
在本地注册为LocalName
。{Name,Node}
-gen_server
在另一个节点上本地注册。{global, GlobalName}
-gen_server
在global
中全局注册。{via, RegMod, ViaName}
-gen_server
在备用进程注册表中注册。请参阅为server_name/0
描述的相同术语。
-type start_mon_ret() :: {ok, {Pid :: pid(), MonRef :: reference()}} | ignore | {error, Reason :: term()}.
来自 start_monitor/3,4
函数的返回值。
与类型 start_ret/0
相同,但对于成功启动,它同时返回进程标识符 Pid
和 monitor/2,3
MonRef
。
-type start_opt() :: {timeout, Timeout :: timeout()} | {spawn_opt, SpawnOptions :: [proc_lib:start_spawn_option()]} | enter_loop_opt().
用于 start
函数的服务器启动选项。
通过例如 start_link/3,4
启动 gen_server
服务器时可以使用的选项。
{timeout, Timeout}
- 允许gen_server
进程花费多少毫秒进行初始化,否则它将被终止,并且启动函数返回{error, timeout}
。{spawn_opt, SpawnOptions}
-SpawnOptions
选项列表被传递给用于派生gen_server
的函数;请参阅proc_lib:start_spawn_option/0
)。注意
不允许使用派生选项
monitor
- 它会导致badarg
错误。enter_loop_opt/0
- 有关enter_loop/3,4,5
也允许的更多启动选项,请参阅下面的类型enter_loop_opt/0
。
来自 start/3,4
和 start_link/3,4
函数的返回值。
{ok, Pid}
-gen_server
进程已成功创建和初始化,进程标识符为Pid
。{error, {already_started, OtherPid}}
- 具有指定ServerName
的进程已经存在,进程标识符为OtherPid
。此函数无法启动gen_server
。它在调用Module:init/1
之前以原因normal
退出。{error, timeout}
-gen_server
进程初始化失败,因为Module:init/1
未在 启动超时 内返回。gen_server
进程被exit(_, kill)
杀死。ignore
-gen_server
进程初始化失败,因为Module:init/1
返回了ignore
。{error,Reason}
-gen_server
进程初始化失败,因为Module:init/1
返回了{stop,Reason}
、{error,Reason}
,或者因为原因Reason
而失败。
有关 gen_server
进程初始化失败时的退出原因,请参阅 Module:init/1
。
回调
-callback code_change(OldVsn :: term() | {down, term()}, State :: term(), Extra :: term()) -> {ok, NewState :: term()} | {error, Reason :: term()}.
在代码更改后更新服务器状态。
当 gen_server
进程要在发布升级/降级期间更新其内部状态时(即,当在 appup
文件中指定指令 {update, Module, Change, ...}
时),将调用此函数。
有关更多信息,请参阅 OTP 设计原则中的 发布处理说明 部分。
对于升级,OldVsn
为 Vsn
,对于降级,OldVsn
为 {down,Vsn}
。Vsn
由旧版本回调模块 Module
的 vsn
属性定义。如果未定义此类属性,则该版本是 Beam 文件的校验和。
State
是 gen_server
进程的内部状态。
Extra
从更新指令的 {advanced,Extra}
部分“原样”传递。
如果成功,该函数必须返回更新后的内部状态。
如果该函数返回 {error,Reason}
,则正在进行的升级将失败并回滚到旧版本。
注意
如果在未实现
Module:code_change/3
时,在.appup
文件中指定了Change = {advanced, Extra}
的发布升级/降级,则回调调用将崩溃并出现undef
错误原因。
-callback format_status(Status) -> NewStatus when Status :: format_status(), NewStatus :: format_status().
格式化/限制状态值。
为了调试和日志记录目的,gen_server
进程会调用此函数来格式化/限制服务器状态。
在以下情况下调用它
- 调用
sys:get_status/1,2
获取gen_server
状态。 gen_server
进程异常终止并记录错误。
此回调用于限制由 sys:get_status/1,2
返回或发送到 logger
的进程状态。
回调接收一个描述当前状态的映射 Status
,并应返回一个具有相同键的映射 NewStatus
,但它可以转换某些值。
此回调的两个可能用例是从状态中删除敏感信息以防止其打印在日志文件中,或者压缩仅会使日志混乱的大型无关状态项。
示例
format_status(Status) ->
maps:map(
fun(state,State) ->
maps:remove(private_key, State);
(message,{password, _Pass}) ->
{password, removed};
(_,Value) ->
Value
end, Status).
注意
此回调是可选的,因此回调模块不必导出它。
gen_server
模块提供了此函数的默认实现,该实现返回回调模块状态。如果此回调已导出但失败,为了隐藏可能敏感的数据,默认函数将改为返回
Module:format_status/1
已崩溃的事实。
-callback format_status(Opt, StatusData) -> Status when Opt :: normal | terminate, StatusData :: [PDict | State], PDict :: [{Key :: term(), Value :: term()}], State :: term(), Status :: term().
格式化/限制状态值。
为了调试和日志记录目的,gen_server
进程会调用此函数来格式化/限制服务器状态。
在以下情况下调用它
- 调用
sys:get_status/1,2
之一来获取gen_server
状态。Opt
设置为原子normal
。 gen_server
进程异常终止并记录错误。Opt
设置为原子terminate
。
此函数对于更改这些情况下 gen_server
状态的形式和外观非常有用。希望更改 sys:get_status/1,2
返回值以及其状态在终止错误日志中的显示方式的回调模块,将导出 Module: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
返回值的其余部分保持一致。
此函数的一个用途是返回紧凑的备用状态表示,以避免在日志文件中打印大型状态项。
注意
此回调是可选的,因此回调模块不必导出它。
gen_server
模块提供了此函数的默认实现,该实现返回回调模块状态。
-callback handle_call(Request :: term(), From :: from(), State :: term()) -> {reply, Reply :: term(), NewState :: term()} | {reply, Reply :: term(), NewState :: term(), timeout() | hibernate | {continue, term()}} | {noreply, NewState :: term()} | {noreply, NewState :: term(), timeout() | hibernate | {continue, term()}} | {stop, Reason :: term(), Reply :: term(), NewState :: term()} | {stop, Reason :: term(), NewState :: term()}.
处理调用。
每当 gen_server
进程收到使用 call/2,3
、multi_call/2,3,4
或 send_request/2,4
发送的请求时,都会调用此函数来处理该请求。
State
是 gen_server
进程的内部状态,NewState
是可能更新的状态。
Request
是从提供给 call
或 multi_call
的相同参数传递的。
返回值 Result
的解释如下
{reply,Reply,NewState}
{reply,Reply,NewState,_}
-Reply
值会发送回客户端请求,并成为其返回值。gen_server
进程会继续使用可能更新的内部状态NewState
执行。{noreply,NewState}
{noreply,NewState,_}
-gen_server
进程会继续使用可能更新的内部状态NewState
执行。必须通过在此回调或以后的回调中调用
reply(From, Reply)
来创建对客户端请求的回复。{reply,_,_,Timeout}
{noreply,_,Timeout}
- 如果提供了整数Timeout
,则除非在该毫秒数内收到请求或消息,否则会发生超时。超时由原子timeout
表示,并由Module:handle_info/2
回调函数处理。Timeout =:= infinity
可用于无限期等待,这与返回不带Timeout
成员的值相同。{reply,_,_,hibernate}
{noreply,_,hibernate}
- 进程通过调用proc_lib:hibernate/3
进入休眠状态,等待下一条消息到达{reply,_,_,{continue,Continue}}
{noreply,_,{continue,Continue}}
- 进程将执行Module:handle_continue/2
回调函数,其中Continue
作为第一个参数。{stop,Reason,NewState}
{stop,Reason,Reply,NewState}
-gen_server
进程将调用Module:terminate(Reason,NewState)
,然后终止。{stop,_,Reply,_}
将像{reply,Reply,...}
一样创建对客户端请求的回复,而{stop,_,_}
则不会,因此与{noreply,NewState,...}
类似,必须在返回{stop,_,_}
之前通过调用reply(From, Reply)
来创建回复。
-callback handle_cast(Request :: term(), State :: term()) -> {noreply, NewState :: term()} | {noreply, NewState :: term(), timeout() | hibernate | {continue, term()}} | {stop, Reason :: term(), NewState :: term()}.
处理投射消息。
每当 gen_server
进程收到使用 cast/2
或 abcast/2,3
发送的请求时,都会调用此函数来处理该请求。
有关参数和可能的返回值的说明,请参阅 Module:handle_call/3
。
-callback handle_continue(Info :: term(), State :: term()) -> {noreply, NewState :: term()} | {noreply, NewState :: term(), timeout() | hibernate | {continue, term()}} | {stop, Reason :: term(), NewState :: term()}.
处理回调延续。
当先前的回调返回包含 {continue, Continue}
的元组之一时,gen_server
进程会调用此函数。此调用会在先前的回调之后立即调用,这使得它可用于在初始化后执行工作,或将回调中的工作拆分为多个步骤,同时更新进程状态。
有关其他参数和可能的返回值的说明,请参阅 Module:handle_call/3
。
注意
此回调是可选的,因此回调模块仅在从其他回调返回包含
{continue,Continue}
的元组之一时才需要导出它。如果使用此类{continue,_}
元组,并且未实现回调,则进程将以undef
错误退出。
-callback handle_info(Info :: timeout | term(), State :: term()) -> {noreply, NewState :: term()} | {noreply, NewState :: term(), timeout() | hibernate | {continue, term()}} | {stop, Reason :: term(), NewState :: term()}.
处理信息消息(常规进程消息)。
当发生超时或接收到除同步或异步请求(或系统消息)以外的任何其他消息时,gen_server
进程会调用此函数。
Info
要么是原子 timeout
(如果发生了超时),要么是接收到的消息。
有关其他参数和可能的返回值的说明,请参阅 Module:handle_call/3
。
注意
此回调是可选的,因此回调模块不需要导出它。
gen_server
模块提供了此函数的默认实现,该实现会记录有关意外Info
消息的信息、删除该消息并返回{noreply, State}
。
-callback init(Args :: term()) -> {ok, State :: term()} | {ok, State :: term(), timeout() | hibernate | {continue, term()}} | {stop, Reason :: term()} | ignore | {error, Reason :: term()}.
初始化服务器。
每当使用 start/3,4
、start_monitor/3,4
或 start_link/3,4
启动 gen_server
进程时,新进程都会调用此函数来初始化服务器。
Args
是提供给 start 函数的 Args
参数。
返回值 Result
的解释如下
{ok,State}
{ok,State,_}
- 初始化成功,并且State
是gen_server
进程的内部状态。{ok,_,Timeout}
{ok,_,hibernate}
{ok,_,{continue,Continue}}
- 有关此元组成员的说明,请参阅Module:handle_call/3
中的相应返回值。{stop,Reason}
- 初始化失败。gen_server
进程以Reason
为原因退出。{error,Reason}
自 OTP 26.0 起ignore
- 初始化失败。gen_server
进程以normal
为原因退出。
在这些不同情况下,请参阅函数 start_link/3,4
的返回值 start_ret/0
。
-callback terminate(Reason :: normal | shutdown | {shutdown, term()} | term(), State :: term()) -> term().
处理服务器终止。
当 gen_server
进程即将终止时,会调用此函数。
它是 Module:init/1
的对应项,并且会执行任何必要的清理。当它返回时,gen_server
进程会以 Reason
终止。返回值会被忽略。
Reason
是表示停止原因的术语,State
是 gen_server
进程的内部状态。
Reason
取决于 gen_server
进程终止的原因。如果是由于另一个回调函数返回了 stop 元组 {stop,..}
,则 Reason
的值是该元组中指定的值。如果是由于发生故障,则 Reason
是错误原因。
如果 gen_server
进程是监督树的一部分,并且其主管命令它终止,则如果满足以下条件,则会使用 Reason=shutdown
调用此函数
- 已将
gen_server
进程设置为捕获退出信号。 - 主管的子规范中定义的关闭策略是整数超时值,而不是
brutal_kill
。
即使 gen_server
进程不是监督树的一部分,如果它从其父进程收到 'EXIT'
消息,也会调用此函数。Reason
与 'EXIT'
消息中的原因相同。
如果 gen_server
进程不捕获退出,则 gen_server
进程会立即终止。
请注意,对于除 normal
、shutdown
或 {shutdown,Term}
以外的任何其他原因,请参阅 stop/3
,假设 gen_server
进程是由于错误而终止的,并且会使用 logger
发出错误报告。
当 gen_server 进程退出时,会向链接的进程和端口发送具有相同原因的退出信号。
注意
此回调是可选的,因此回调模块不需要导出它。
gen_server
模块提供了不进行清理的默认实现。
函数
将请求投射到多个节点。
等效于 abcast(Nodes, Name, Request)
,其中 Nodes
是连接到调用节点的所有节点,包括调用节点本身。
将请求投射到多个节点。
将异步请求发送到指定节点上本地注册为 Name
的 gen_server
进程。该函数会立即返回,并忽略不存在的节点,或 gen_server
Name
不存在的节点。gen_server
进程会调用 Module:handle_cast/2
来处理该请求。
有关参数的说明,请参阅 multi_call/2,3,4
。
-spec call(ServerRef :: server_ref(), Request :: term()) -> Reply :: term().
-spec call(ServerRef :: server_ref(), Request :: term(), Timeout :: timeout()) -> Reply :: term().
调用服务器:发送请求并等待响应。
通过发送请求并等待直到收到回复或发生超时,对 gen_server
进程的 ServerRef
进行同步调用。gen_server
进程会调用 Module:handle_call/3
来处理该请求。
另请参阅 ServerRef
的类型 server_ref/0
。
Request
是作为第一个参数传递给 Module:handle_call/3
的任何项。
Timeout
是一个整数,指定等待回复的毫秒数,或者使用原子 infinity
无限期等待。如果在指定时间内没有收到回复,此函数将使用包含 Reason = timeout
的退出项退出调用进程,如下所述。
注意
在 OTP 24 之前,如果调用者使用 (
try
...)catch
来避免进程退出,并且服务器恰好回复较晚,则它可能稍后到达进程消息队列。因此,调用进程在捕获超时退出后,必须准备好接收{reference(), _}
形式的垃圾消息,并适当处理它们(丢弃它们),以防止它们阻塞进程消息队列,或被误认为其他消息。从 OTP 24 开始,
gen_server:call
使用进程别名,因此不会收到迟到的回复。
返回值 Reply
从 Module:handle_call/3
的返回值传递而来。
此调用可能会使调用进程退出,退出项的形式为 {Reason, Location}
,其中 Location = {gen_server, call, ArgList}
,Reason
可以是(至少)以下之一:
timeout
- 如上所述,在等待Timeout
毫秒以获得回复后,调用被中止。noproc
-ServerRef
通过名称引用服务器(它不是pid/0
),并且查找服务器进程失败,或者pid/0
已经终止。{nodedown,Node}
-ServerRef
引用远程节点Node
上的服务器,并且与该节点的连接失败。calling_self
- 对self/0
的调用将无限期地挂起。shutdown
- 服务器在调用期间被其主管停止。另请参阅stop/3
。normal
{shutdown,Term}
- 服务器在调用期间停止,原因是从其回调之一返回了{stop,Reason,_}
而没有回复此调用。另请参阅stop/3
。_OtherTerm
- 服务器进程在调用期间退出,原因是Reason
。可能是因为从其回调之一返回了{stop,Reason,_}
(没有回复此调用),引发了异常,或者由于收到它没有捕获的退出信号。
-spec cast(ServerRef :: server_ref(), Request :: term()) -> ok.
将请求投射到服务器。
向 gen_server
ServerRef
发送异步请求并立即返回 ok
,忽略目标节点或 gen_server
进程是否存在。
gen_server
进程调用 Module:handle_cast(Request, _)
来处理请求。
-spec check_response(Msg, ReqId) -> Result when Msg :: term(), ReqId :: request_id(), Response :: {reply, Reply :: term()} | {error, {Reason :: term(), server_ref()}}, Result :: Response | no_reply.
检查收到的消息是否为请求响应。
检查 Msg
是否是与请求标识符 ReqId
对应的响应。请求必须由 send_request/2
发出,并且由调用此函数的同一进程发出。
如果 Msg
是对句柄 ReqId
的回复,则请求的结果将在 Reply
中返回。否则,此函数返回 no_reply
并且不进行清理,因此应重复调用该函数,直到返回响应。
返回值 Reply
从 Module:handle_call/3
的返回值传递而来。
如果调用此函数时 gen_statem
服务器进程已死亡,即;Msg
报告服务器的死亡,则此函数将返回带有退出 Reason
的 error
返回值。
-spec check_response(Msg, ReqIdCollection, Delete) -> Result when Msg :: term(), ReqIdCollection :: request_id_collection(), Delete :: boolean(), Response :: {reply, Reply :: term()} | {error, {Reason :: term(), server_ref()}}, Result :: {Response, Label :: term(), NewReqIdCollection :: request_id_collection()} | no_request | no_reply.
检查收到的消息是否为集合中的请求响应。
检查 Msg
是否是与存储在 ReqIdCollection
中的请求标识符对应的响应。ReqIdCollection
的所有请求标识符必须对应于由调用此函数的进程使用 send_request/2
或 send_request/4
发出的请求。
响应中的 Label
等于与响应对应的请求标识符关联的 Label
。Label
在 将请求 ID 存储在集合中时,或在使用 send_request/4
发送请求时与请求标识符关联。
与 check_response/2
相比,与特定请求标识符关联的返回结果或异常将包装在 3 元组 {Response, Label, NewReqIdCollection}
中。Response
是 check_response/2
将产生的值,Label
是与特定 请求标识符 关联的值,NewReqIdCollection
是可能已修改的请求标识符集合。
如果 ReqIdCollection
为空,将返回 no_request
。
如果 Msg
不对应于 ReqIdCollection
中的任何请求标识符,则返回 no_reply
。
如果 Delete
为 true
,则与 Label
的关联已从生成的 NewReqIdCollection
中的 ReqIdCollection
中删除。如果 Delete
为 false
,则 NewReqIdCollection
将等于 ReqIdCollection
。请注意,删除关联并非没有代价,并且包含已处理请求的集合仍可由后续的 check_response/3
、receive_response/3
和 wait_response/3
调用使用。
但是,如果不删除已处理的关联,则以上调用将无法检测何时没有更多未处理的请求,因此您必须以其他方式跟踪此信息,而不是依赖于 no_request
返回值。请注意,如果您将仅包含已处理或放弃的请求的关联的集合传递给此函数,它将始终返回 no_reply
。
-spec enter_loop(Module :: module(), Options :: [enter_loop_opt()], State :: term()) -> no_return().
-spec enter_loop(Module :: module(), Options :: [enter_loop_opt()], State :: term(), ServerName :: server_name() | pid()) -> no_return(); (Module :: module(), Options :: [enter_loop_opt()], State :: term(), How :: timeout() | hibernate | {continue, term()}) -> no_return().
使调用进程成为 gen_server
进程。
使用与 enter_loop(Module, Options, State, ServerName, infinity)
等效的参数 ServerName
。
使用与 enter_loop(Module, Options, State, self(), How)
等效的参数 How
。
-spec enter_loop(Module :: module(), Options :: [enter_loop_opt()], State :: term(), ServerName :: server_name() | pid(), Timeout :: timeout()) -> no_return(); (Module :: module(), Options :: [enter_loop_opt()], State :: term(), ServerName :: server_name() | pid(), Hibernate :: hibernate) -> no_return(); (Module :: module(), Options :: [enter_loop_opt()], State :: term(), ServerName :: server_name() | pid(), Cont :: {continue, term()}) -> no_return().
使调用进程成为 gen_server
进程。
不会返回,而是调用进程进入 gen_server
进程接收循环并成为 gen_server
进程。该进程必须已使用 proc_lib
中的启动函数之一启动。用户负责进程的任何初始化,包括为其注册名称。
当需要比 gen_server
Module:init/1
回调提供的更复杂的初始化过程时,此函数很有用。
Module
、Options
和 ServerName
的含义与调用 start[_link|_monitor]/3,4
时相同,或者 ServerName
可以是匿名服务器的 self/0
,这与调用没有 ServerName
参数的 enter_loop/3,4
函数相同。但是,如果指定了 ServerName
(而不是 self/0
),则必须在调用此函数之前按照相应方式注册该进程。
State
、Timeout
、Hibernate
和 Cont
的含义与 Module:init/1
的返回值中的含义相同,当使用 enter_loop/3,4,5
时,不会调用该返回值。请注意,为了遵守 gen_server 行为,需要定义这样的回调函数,并且它也可以是在通过 proc_lib
启动 gen_server
进程时使用的函数,然后调用 enter_loop/3,4,5
。但是,如果这样的 Module:init/1
函数(例如,在错误情况下)无法调用 enter_loop/3,4,5
,则它应返回符合 Module:init/1
类型规范的值,例如 ignore
,尽管该值在返回到生成函数时将丢失。
如果调用进程不是由 proc_lib
启动函数启动的,或者如果没有按照 ServerName
进行注册,则此函数将失败。
-spec multi_call(Name :: atom(), Request :: term()) -> {Replies :: [{Node :: node(), Reply :: term()}], BadNodes :: [node()]}.
并行调用多个节点上的服务器。
等效于 multi_call(Nodes, Name, Request)
,其中 Nodes
是所有连接到调用节点的节点,包括调用节点本身。
-spec multi_call(Nodes :: [node()], Name :: atom(), Request :: term(), Timeout :: timeout()) -> {Replies :: [{Node :: node(), Reply :: term()}], BadNodes :: [node()]}.
并行调用多个节点上的服务器。
通过先将请求发送到节点,然后等待回复,对指定节点上本地注册为 Name
的所有 gen_server
进程进行同步调用。节点上的 gen_server
进程调用 Module:handle_call/3
来处理请求。
该函数返回一个元组 {Replies, BadNodes}
,其中 Replies
是 {Node, Reply}
元组的列表,BadNodes
是不存在的节点列表,其中 Name
不是注册的 gen_server
,或者没有回复。
Nodes
是要将请求发送到的节点名称的列表。
Name
是每个 gen_server
进程的本地注册名称。
Request
是作为第一个参数传递给 Module:handle_call/3
的任何项。
Timeout
是一个整数,指定等待所有回复的毫秒数,或者使用原子 infinity
无限期等待。如果在指定时间内没有收到来自节点的回复,则该节点将添加到 BadNodes
。
当从节点 Node
上的 gen_server
进程收到回复 Reply
时,{Node,Reply}
将添加到 Replies
。Reply
从 Module:handle_call/3
的返回值传递而来。
警告
如果其中一个节点无法处理监视器(例如,C 或 Java 节点),并且在发送请求时未启动
gen_server
进程,但在 2 秒内启动,则此函数将等待整个Timeout
,这可能是无限的。如果所有节点都是 Erlang 节点,则不存在此问题。
为了防止超时后的迟到回复污染调用者的消息队列,使用中间人进程来执行调用。当迟到回复到达已终止的中间人进程时,会被丢弃。
-spec receive_response(ReqId, Timeout) -> Result when ReqId :: request_id(), Timeout :: response_timeout(), Response :: {reply, Reply :: term()} | {error, {Reason :: term(), server_ref()}}, Result :: Response | timeout.
接收请求响应。
接收与请求标识符 ReqId
对应的响应。该请求必须由 send_request/2
发出,并且必须由调用此函数的同一进程发出。
Timeout
指定等待响应的时间。如果在指定的时间内未收到响应,此函数将返回 timeout
。假设服务器在支持别名(在 OTP 24 中引入)的节点上执行,则该请求也将被放弃。也就是说,超时后将不会收到任何响应。否则,可能会在稍后收到一个滞后的响应。
返回值 Reply
从 Module:handle_call/3
的返回值传递而来。
如果 gen_server
在发送回复之前死亡,则该函数将返回错误。
receive_response/2
和 wait_response/2
之间的区别在于,receive_response/2
在超时时放弃请求,以便忽略潜在的未来响应,而 wait_response/2
则不会。
-spec receive_response(ReqIdCollection, Timeout, Delete) -> Result when ReqIdCollection :: request_id_collection(), Timeout :: response_timeout(), Delete :: boolean(), Response :: {reply, Reply :: term()} | {error, {Reason :: term(), server_ref()}}, Result :: {Response, Label :: term(), NewReqIdCollection :: request_id_collection()} | no_request | timeout.
接收集合中的请求响应。
在 ReqIdCollection
中接收响应。ReqIdCollection
的所有请求标识符都必须对应于使用 send_request/2
或 send_request/4
发出的请求,并且所有请求都必须由调用此函数的进程发出。
响应中的 Label
是与响应对应的请求标识符关联的 Label
。当 将请求 ID 添加到集合或使用 send_request/4
发送请求时,将关联请求标识符的 Label
。
与 receive_response/2
相比,与特定请求标识符关联的返回结果或异常将包装在 3 元组 {Response, Label, NewReqIdCollection}
中。Response
是 receive_response/2
将会产生的值,Label
是与特定 请求标识符 关联的值,NewReqIdCollection
是一个可能经过修改的请求标识符集合。
如果 ReqIdCollection
为空,将返回 no_request
。
Timeout
指定等待响应的时间。如果在指定的时间内未收到响应,该函数将返回 timeout
。假设服务器在支持别名(在 OTP 24 中引入)的节点上执行,则 ReqIdCollection
标识的所有请求也将被放弃。也就是说,超时后将不会收到任何响应。否则,可能会在稍后收到滞后的响应。
receive_response/3
和 wait_response/3
之间的区别在于,receive_response/3
在超时时放弃请求,以便忽略潜在的未来响应,而 wait_response/3
则不会。
如果 Delete
为 true
,则在生成的 NewReqIdCollection
中,将从 ReqIdCollection
中删除与 Label
的关联。如果 Delete
为 false
,则 NewReqIdCollection
将等于 ReqIdCollection
。请注意,删除关联并非没有代价,并且包含已处理请求的集合仍然可以被后续调用 receive_response/3
、check_response/3
和 wait_response/3
使用。
但是,如果不删除已处理的关联,上述调用将无法检测到何时没有更多待处理的请求,因此您必须以其他方式跟踪此情况,而不是依赖 no_request
返回值。请注意,如果您将仅包含已处理或已放弃请求的关联的集合传递给此函数,它将始终阻塞,直到 Timeout
过期,然后返回 timeout
。
向客户端发送回复。
当回复无法在 Module:handle_call/3
的返回值中传递时,gen_server
进程可以使用此函数显式地向调用 call/2,3
或 multi_call/2,3,4
的客户端发送回复。
Client
必须是提供给 handle_call/3
回调函数的 From
参数。Reply
是作为 call/2,3
或 multi_call/2,3,4
的返回值传递回客户端的任何项。
-spec reqids_add(ReqId :: request_id(), Label :: term(), ReqIdCollection :: request_id_collection()) -> NewReqIdCollection :: request_id_collection().
在集合中存储请求标识符。
存储 ReqId
并通过将此信息添加到 ReqIdCollection
并返回生成的请求标识符集合来将 Label
与请求标识符关联。
-spec reqids_new() -> NewReqIdCollection :: request_id_collection().
创建空的请求标识符集合。
返回一个新的空请求标识符集合。请求标识符集合可用于处理多个未完成的请求。
使用 send_request/2
发出的请求的请求标识符可以使用 reqids_add/3
存储在集合中。稍后,可以通过将集合作为参数传递给 receive_response/3
、wait_response/3
或 check_response/3
,来获取集合中与请求对应的响应。
reqids_size/1
可用于确定集合中请求标识符的数量。
-spec reqids_size(ReqIdCollection :: request_id_collection()) -> non_neg_integer().
返回 ReqIdCollection
中的请求标识符的数量。
-spec reqids_to_list(ReqIdCollection :: request_id_collection()) -> [{ReqId :: request_id(), Label :: term()}].
将请求标识符集合转换为列表。
返回 {ReqId, Label}
元组的列表,这些元组对应于 ReqIdCollection
中所有请求标识符及其关联的标签。
-spec send_request(ServerRef :: server_ref(), Request :: term()) -> ReqId :: request_id().
发送异步 call
请求。
将 Request
发送到由 ServerRef
标识的 gen_server
进程,并返回一个请求标识符 ReqId
。
返回值 ReqId
稍后应与 receive_response/2
、wait_response/2
或 check_response/2
一起使用,以获取请求的实际结果。除了将请求标识符直接传递给这些函数外,还可以使用 reqids_add/3
将其存储在请求标识符集合中。稍后,可以通过将集合作为参数传递给 receive_response/3
、wait_response/3
或 check_response/3
,来获取集合中与请求对应的响应。如果要将请求标识符存储在集合中,您可能需要考虑使用 send_request/4
。
调用 gen_server:receive_response(gen_server:send_request(ServerRef, Request), Timeout)
可以被视为等效于 gen_server:call(ServerRef, Request, Timeout)
,忽略错误处理。
gen_server
进程调用 Module:handle_call/3
来处理请求。
有关 ServerRef
的可能值,请参见类型 server_ref/0
。
Request
是作为第一个参数传递给 Module:handle_call/3
的任何项。
-spec send_request(ServerRef :: server_ref(), Request :: term(), Label :: term(), ReqIdCollection :: request_id_collection()) -> NewReqIdCollection :: request_id_collection().
发送异步 call
请求,并将其添加到请求标识符集合。
将 Request
发送到由 ServerRef
标识的 gen_server
进程。该 Label
将与操作的请求标识符关联,并添加到返回的请求标识符集合 NewReqIdCollection
中。稍后,可以通过将集合作为参数传递给 receive_response/3
、wait_response/3
或 check_response/3
,来获取集合中与请求对应的响应。
与调用 reqids_add
(
send_request
(ServerRef, Request), Label, ReqIdCollection)
相同,但效率略高。
启动服务器,既不链接也不注册。
-spec start(ServerName :: server_name(), Module :: module(), Args :: term(), Options :: [start_opt()]) -> start_ret().
启动服务器,既不链接也不注册。
创建一个独立的 gen_server
进程,即不属于监督树的 gen_server
进程,因此没有监督者。
除此之外,请参见 start_link/4
。
启动服务器,已链接但未注册。
与 start_link/4
相同,只是 gen_server
进程未在任何 名称服务中注册。
-spec start_link(ServerName :: server_name(), Module :: module(), Args :: term(), Options :: [start_opt()]) -> start_ret().
启动服务器,已链接但未注册。
创建作为监督树一部分的 gen_server
进程。此函数应由监督者直接或间接地调用。例如,它确保 gen_server
进程作为链接到调用者(监督者)的进程生成。
gen_server
进程调用 Module:init/1
来初始化。为了确保同步的启动过程,start_link/3,4
在 Module:init/1
返回或失败之前不会返回。
ServerName
指定使用哪个名称以及现在如何注册服务器名称。有关不同的名称注册,请参见类型 server_name/0
。
Module
是回调模块的名称。
Args
是作为参数传递给 Module:init/1
的任何项。
有关启动 gen_server
进程的 Options
,请参见类型 start_opt/0
。
有关此函数返回值的说明,请参见类型 start_ret/0
。
如果 start_link/3,4
返回 ignore
或 {error, _}
,则启动的 gen_server
进程已终止。如果 'EXIT'
消息已传递到调用进程(由于进程链接),则该消息已被使用。
警告
在 OTP 26.0 之前,如果启动的
gen_server
进程从Module:init/1
返回例如{stop, Reason}
,则此函数可能在启动的gen_server
进程终止之前返回{error, Reason}
,因此重新启动可能会失败,因为 VM 资源(例如已注册的名称)尚未注销。 一个'EXIT'
消息可能会稍后到达调用此函数的进程。但是,如果启动的
gen_server
进程在Module:init/1
期间失败,则进程链接{'EXIT', Pid, Reason}
消息会导致此函数返回{error, Reason}
,因此'EXIT'
消息已被消耗,并且启动的gen_server
进程已终止。由于无法从
start_link/3,4
的返回值中区分这两种情况,因此这种不一致在 OTP 26.0 中得到了清理。
从 Module:init/1
返回 {stop, _}
和 {error, _}
的区别在于,{error, _}
会导致优雅的(“静默”)终止,因为 gen_server
进程以原因 normal
退出。
-spec start_monitor(Module :: module(), Args :: term(), Options :: [start_opt()]) -> start_mon_ret().
启动服务器,已监视但既不链接也不注册。
等同于 start_monitor/4
,只是 gen_server
进程未在任何 名称服务中注册。
-spec start_monitor(ServerName :: server_name(), Module :: module(), Args :: term(), Options :: [start_opt()]) -> start_mon_ret().
启动服务器,已监视和注册,但未链接。
创建一个独立的 gen_server
进程,即不是监督树的一部分(因此没有监督者)的 gen_server
进程,并原子地设置对新创建的服务器的监视器。
除此之外,请参见 start_link/3,4
。请注意,成功启动的返回值不同,它返回一个监视器 reference
。请参见类型 start_mon_ret/0
。
如果启动不成功,则调用方将被阻塞,直到收到监视器的 'DOWN'
消息并将其从消息队列中移除。
-spec stop(ServerRef :: server_ref()) -> ok.
-spec stop(ServerRef :: server_ref(), Reason :: term(), Timeout :: timeout()) -> ok.
停止服务器。
命令由 ServerRef
指定的通用服务器以指定的 Reason
退出,并等待它终止。 gen_server
进程在退出前调用 Module:terminate/2
。
如果服务器以预期的原因终止,则该函数返回 ok
。除 normal
、shutdown
或 {shutdown,Term}
之外的任何其他原因都会使用 logger
发出错误报告。具有相同原因的退出信号会发送到链接的进程和端口。
Timeout
是一个整数,指定等待服务器终止的毫秒数,或者是原子 infinity
以无限期等待。如果服务器在指定的时间内没有终止,则该调用将以原因 timeout
退出调用进程。
如果该进程不存在,则该调用将以原因 noproc
退出调用进程,或者如果与运行服务器的远程 Node
的连接失败,则以原因 {nodedown,Node}
退出。
-spec wait_response(ReqId, WaitTime) -> Result when ReqId :: request_id(), WaitTime :: response_timeout(), Response :: {reply, Reply :: term()} | {error, {Reason :: term(), server_ref()}}, Result :: Response | timeout.
等待请求响应。
等待对请求标识符 ReqId
的响应。该请求必须由 send_request/2
发出,并且必须由调用此函数的同一进程发出。
WaitTime
指定等待回复的时间。如果在指定时间内未收到回复,则该函数返回 timeout
,并且不进行清理。因此,可以重复调用该函数,直到返回回复为止。
返回值 Reply
从 Module:handle_call/3
的返回值传递而来。
如果 gen_server
在发送回复之前死亡,则该函数将返回错误。
receive_response/2
和 wait_response/2
之间的区别在于,receive_response/2
在超时时放弃请求,以便忽略潜在的未来响应,而 wait_response/2
则不会。
-spec wait_response(ReqIdCollection, WaitTime, Delete) -> Result when ReqIdCollection :: request_id_collection(), WaitTime :: response_timeout(), Delete :: boolean(), Response :: {reply, Reply :: term()} | {error, {Reason :: term(), server_ref()}}, Result :: {Response, Label :: term(), NewReqIdCollection :: request_id_collection()} | no_request | timeout.
等待集合中的任何请求响应。
在 ReqIdCollection
中等待响应。 ReqIdCollection
的所有请求标识符必须对应于使用 send_request/2
或 send_request/4
发出的请求,并且所有请求必须由调用此函数的进程发出。
响应中的 Label
是与响应对应的请求标识符关联的 Label
。当 将请求 ID 添加到集合或使用 send_request/4
发送请求时,将关联请求标识符的 Label
。
与 wait_response/2
相比,与特定请求标识符关联的返回结果或异常将包装在 3 元组 {Response, Label, NewReqIdCollection}
中。Response
是 wait_response/2
本应产生的值,Label
是与特定 请求标识符 关联的值,而 NewReqIdCollection
是可能已修改的请求标识符集合。
如果 ReqIdCollection
为空,将返回 no_request
。
如果在 WaitTime
过期之前没有收到响应,则返回 timeout
。在收到响应并由 check_response()
、receive_response()
或 wait_response()
完成之前,可以根据需要多次继续等待响应。
receive_response/3
和 wait_response/3
之间的区别在于 receive_response/3
会在超时时放弃请求,以便忽略潜在的未来响应,而 wait_response/3
则不会。
如果 Delete
为 true
,则与 Label
的关联已从结果 NewReqIdCollection
中的 ReqIdCollection
中删除。如果 Delete
为 false
,则 NewReqIdCollection
将等于 ReqIdCollection
。请注意,删除关联并非没有代价,并且包含已处理请求的集合仍然可以被后续对 wait_response/3
、check_response/3
和 receive_response/3
的调用使用。
但是,如果不删除已处理的关联,则上述调用将无法检测何时没有更多待处理的请求要处理,因此您必须以其他方式跟踪此信息,而不是依赖 no_request
返回。请注意,如果您将仅包含已处理或放弃的请求的关联的集合传递给此函数,它将始终阻塞,直到 WaitTime
过期,然后返回 timeout
。