gen_server
gen_server
是Erlang OTP中的通用服务器模板。
gen_server
behavior函数和回调函数之间的关系如下:
gen_server module Callback module
----------------- ---------------
gen_server:start
gen_server:start_link -----> Module:init/1
gen_server:stop -----> Module:terminate/2
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:start(Module, Args, Options)
或gen_server:start(ServerName, Module, Args, Options)
或gen_server:start_link(Module, Args, Options)
或gen_server:start_link(ServerName, Module, Args, Options)
启动一个gen_server
进程,start
与start_link
的区别在于用start启动的进程相当于创建一个独立的进程,当需要在监控树中启动时,需要使用start_link
。
ServerName
指定了进程的名字,格式可以是{local, Name},{global, Name}
以及{via, Module, Name}
,可不指定;
Module
指定了gen_sever
进程的回调模块;
如果ServerName
为{local, Name}
,会通过register/2
将进程注册为Name
。为{global, Name}
则通过global:register_name/2
将进程注册为Name
。
Args
会作为参数传递给Module:init(Args)
;
Options
为其他可选项。
gen_server
进程启动时会调用Module:init(Args)
对进程数据进行初始化处理,如果成功则会返回{ok,State}
|{ok,State,TimeOut}
|{ok,State,hibernate}
,State
为进程数据;TimeOut
表示进程如果TimeOu
t ms内没有收到消息,将会触发一个超时,超时消息将会被Module:handle_info(timeout, State)
处理;hibernate
表示进程将会挂起直至下一个消息到来。启动失败则会返回{stop, Reason}
或ignore
。由于start_link
为同步调用,在init
返回前它是不会返回的。
可以通过gen_server:stop(ServerRef)
或stop(ServerRef, Reason, TimeOut)
去结束一个gen_server
进程。
gen_server
进程在退出前会先调用Module:terminate(Reason, State)
善后。ServerRef
为gen_server
进程的Pid
或进程名。
对于属于监控树的gen_server
,需要满足在以下条件才会在进程终止前调用terminate/2
:
- 在
init
中调用process_flag(trap_exit, true)
; - 监控树设定的关闭策略被设定为一个超时值,而非直接
kill
掉。
不属于监控树时,当gen_server
进程在收到父进程的'Exit'
消息时也会调用该方法。
可以通过gen_server:call(ServerRef, Request)
或gen_server:call(ServerRef, Request, TimeOut)
向gen_server
进程同步发送一个消息并等待返回,超过TimeOut
(默认为5000) ms未返回则会导致调用失败。gen_server
进程通过调用Module:handle_call(Request, From, State)
处理该消息并返回{reply, Reply, NewState}
,Reply
会返还给调用进程。
可以通过gen_server:cast(ServerRef, Request)
向gen_server
进程异步发送一个消息并立即返回ok
。gen_server
进程通过调用Module:handle_cast(Request, State)
处理该消息并返回{noreply, NewState}
。
另外,可以通过Pid!Info
或者erlang:send(Pid, Info)
给gen_server
进程发送消息,gen_server
将会通过Module:handle_info(Info, State)
来处理该消息。
一个简单gen_server
模板(自《Erlang程序设计》)
-module(gen_server_mudule).
-behaviour(gen_server).
%% API
-export([start_link/0]).
%% gen_server回调函数
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
%%%===================================================================
%%% API
%%%===================================================================
%%--------------------------------------------------------------------
%% @doc
%% 启动服务器
%%
%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
%% @end
%%--------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
%%%===================================================================
%%% gen_server回调函数
%%%===================================================================
%%--------------------------------------------------------------------
%% @private
%% @doc
%% 初始化服务器
%%
%% @spec init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% @end
%%--------------------------------------------------------------------
init([]) ->
{ok, []}.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% 处理调用消息
%%
%% @spec handle_call(Request, From, State) ->
%% {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% 处理播发消息
%%
%% @spec handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% 处理所有非调用/播发的消息
%%
%% @spec handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% 这个函数是在某个gen_server即将终止时调用的。它应当是Module:init/1的逆操作,并进行必要的清理。
%% 当它返回时,gen_server终止并生成原因Reason。它的返回值会被忽略
%%
%% @spec terminate(Reason, State) -> void()
%% @end
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% 在代码更改时转换进程状态
%%
%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
%% @end
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%===================================================================
%%% 内部函数
%%%===================================================================