使用 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).