檢視原始碼 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
程序識別碼全域註冊為GlobalName
。{via, RegMod, ViaName}
- 使用RegMod
代表的註冊表註冊gen_server
程序。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_opt/0
,了解enter_loop/3,4,5
也允許的其他啟動選項。
來自 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
而失敗。
請參閱 Module:init/1
,了解 gen_server
程序初始化失敗時的退出原因。
回呼
-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
程序收到同步或非同步要求(或系統訊息)以外的任何其他訊息時,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
是提供給啟動函數的 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,..}
,則 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
報告伺服器已終止,則此函式會傳回 error
回傳值,並帶有終止 Reason
。
-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
。請求識別碼的 Label
在將請求 ID 加入集合時,或使用 send_request/4
發送請求時相關聯。
與 receive_response/2
相比,與特定請求識別碼相關聯的返回結果或異常將被包裝在一個三元組 {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
,則與 Label
的關聯會從結果的 NewReqIdCollection
中的 ReqIdCollection
中刪除。如果 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
返回值的任何 term。
-spec reqids_add(ReqId :: request_id(), Label :: term(), ReqIdCollection :: request_id_collection()) -> NewReqIdCollection :: request_id_collection().
將請求識別碼儲存在集合中。
儲存 ReqId
並將 Label
與請求識別碼關聯,方法是將此資訊加入到 ReqIdCollection
並回傳結果的請求識別碼集合。
-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
來處理請求。
請參閱類型 server_ref/0
以了解 ServerRef
的可能值。
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
。請求識別碼的 Label
在將請求 ID 加入集合時,或使用 send_request/4
發送請求時相關聯。
與 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
。