使用 gen 伺服器行為
gen_server
是一個像伺服器一樣工作的特定有限狀態機。gen_server
可以處理不同型別的事件:
- 與
handle_call
的同步請求 - 與
handle_cast
的非同步請求 handle_info
的其他訊息(未在 OTP 規範中定義)
同步和非同步訊息在 OTP 中指定,並且是帶有任何型別資料的簡單標記元組。
一個簡單的 gen_server
定義如下:
-module(simple_gen_server).
-behaviour(gen_server).
-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
start_link() ->
Return = gen_server:start_link({local, ?MODULE}, ?MODULE, [], []),
io:format("start_link: ~p~n", [Return]),
Return.
init([]) ->
State = [],
Return = {ok, State},
io:format("init: ~p~n", [State]),
Return.
handle_call(_Request, _From, State) ->
Reply = ok,
Return = {reply, Reply, State},
io:format("handle_call: ~p~n", [Return]),
Return.
handle_cast(_Msg, State) ->
Return = {noreply, State},
io:format("handle_cast: ~p~n", [Return]),
Return.
handle_info(_Info, State) ->
Return = {noreply, State},
io:format("handle_info: ~p~n", [Return]),
Return.
terminate(_Reason, _State) ->
Return = ok,
io:format("terminate: ~p~n", [Return]),
ok.
code_change(_OldVsn, State, _Extra) ->
Return = {ok, State},
io:format("code_change: ~p~n", [Return]),
Return.
此程式碼很簡單:收到的每條訊息都列印到標準輸出。
gen_server 行為
要定義 gen_server
,你需要使用 -behaviour(gen_server)
在原始碼中明確宣告它。注意,behaviour
可以用美國(行為)或英國(行為)來寫。
START_LINK / 0
此函式是呼叫另一個函式的簡單快捷方式:gen_server:start_link/3,4
。
START_LINK / 3,4
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
當你想要啟動連結到 supervisor
或其他程序的伺服器時,將呼叫此函式。start_link/3,4
可以自動註冊你的過程(如果你認為你的過程需要是唯一的),或者可以像簡單的過程那樣簡單地生成它。呼叫時,此函式執行 init/1
。
此函式可以返回以下定義值:
{ok,Pid}
ignore
{error,Error}
INIT / 1
init([]) ->
State = [],
{ok, State}.
init/1
是你的伺服器啟動時第一個執行的功能。這個初始化應用程式的所有先決條件並將狀態返回到新建立的程序。
此函式只能返回這些定義的值:
{ok,State}
{ok,State,Timeout}
{ok,State,hibernate}
{stop,Reason}
ignore
State
變數可以是所有東西,(例如,列表,元組,支柱,地圖,記錄),並且對於生成的程序內的所有函式都可以訪問。
handle_call / 3
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
gen_server:call/2
執行此回撥。第一個引數是你的訊息(_Request
),第二個引數是請求的起源(_From
),最後一個引數是你執行的 gen_server 行為的當前狀態(State
)。
如果你想回復呼叫者,handle_call/3
需要返回以下資料結構之一:
{reply,Reply,NewState}
{reply,Reply,NewState,Timeout}
{reply,Reply,NewState,hibernate}
如果你不想回復呼叫者,handle_call/3
需要返回以下資料結構之一:
{noreply,NewState}
{noreply,NewState,Timeout}
{noreply,NewState,hibernate}
如果要停止當前 gen_server 的當前執行,handle_call/3
需要返回以下資料結構之一:
{stop,Reason,Reply,NewState}
{stop,Reason,NewState}
handle_cast / 2
handle_cast(_Msg, State) ->
{noreply, State}.
gen_server:cast/2
執行此回撥。第一個引數是你的訊息(_Msg
),第二個引數是你正在執行的 gen_server 行為的當前狀態。
預設情況下,此函式無法向呼叫者提供資料,因此,你只有兩個選擇,繼續當前執行:
{noreply,NewState}
{noreply,NewState,Timeout}
{noreply,NewState,hibernate}
或者停止當前的 gen_server
流程:
{stop,Reason,NewState}
handle_info / 2
handle_info(_Info, State) ->
{noreply, State}.
當非標準 OTP 訊息來自外部世界時,執行 handle_info/2
。這個不能回覆,像 handle_cast/2
只能做 2 個動作,繼續當前執行:
{noreply,NewState}
{noreply,NewState,Timeout}
{noreply,NewState,hibernate}
或者停止當前執行的 gen_server
程序:
{stop,Reason,NewState}
終止/ 2
terminate(_Reason, _State) ->
ok.
發生錯誤或想要關閉 gen_server
程序時呼叫 terminate/2
。
code_change / 3
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
當你想要升級正在執行的程式碼時,會呼叫 code_change/3
函式。
此函式只能返回這些定義的值:
{ok, NewState}
{error, Reason}
開始這個過程
你可以編譯程式碼並啟動 simple_gen_server
:
simple_gen_server:start_link().
如果要將訊息傳送到伺服器,可以使用以下功能:
% will use handle_call as callback and print:
% handle_call: mymessage
gen_server:call(simple_gen_server, mymessage).
% will use handle_cast as callback and print:
% handle_cast: mymessage
gen_server:cast(simple_gen_server, mymessage).
% will use handle_info as callback and print:
% handle_info: mymessage
erlang:send(whereis(simple_gen_server), mymessage).