一个gen_server服务器在运行周期里面保持了一系列运行状态,erlang根据运行状态来决定是否停止该服务,今天说的是常规方法停服务的方法,至于由异常引起的服务停止,又要分为我们有没有提前做过trap_exit处理,改日再说。
先看一个简单的服务器,什么也不处理,只是启动起来。
- -module(lab).
- -author('[email protected]').
- -compile(export_all).
- -behaviour(gen_server).
- -export([start_link/0]).
- -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
- -record(state, {}).
- start_link() ->
- gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
- init([]) ->
- State = #state{},
- ?DEBUG("~w runing up", [?MODULE]),
- {ok, State}.
- handle_call(_Request, _From, State) ->
- {noreply, State}.
- handle_cast(_Msg, State) ->
- {noreply, State}.
- handle_info(_Info, State) ->
- {noreply, State}.
- terminate(_Reason, _State) ->
- ok.
- code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
停止服务的几种方法
1、异步非阻塞式停止服务器
stop() ->
gen_server:cast(?MODULE, stop).
handle_cast(stop, State) ->
{stop, normal, State};
在erlang虚拟机中用c(lab)编译后,通过lab:start_link()运行服务,通过whereis(lab)可以查看到服务已经成功启动并注册了lab这个名字。我们通过lab:stop()来停止服务,执行成功,通过whereis(lab)查看得到undefined结果,说明服务已经被停止,这种停止方法可行。
2、同步阻塞式停止服务器
将stop函数改为:
stop() ->
gen_server:call(?MODULE, stop).
同时用handle_call处理这条消息:
handle_call(stop,_From, State) ->
{stop, normal, shutdown_ok, State}.
同上面编译运行测试的方法,这样的停止服务器也是ok的。调用stop/0并且成功停止后调用函数会得到shutdown_ok的返回值。
3、当一个模块注册的是global名字时,发送stop消息时,要注意模块名称的一致,具体如下:
start_link() ->
gen_server:start_link({global, ?MODULE}, ?MODULE, [], []).
stop() ->
gen_server:call({global, ?MODULE}, stop).
另外一个区别就是,通过注册名称查看进程id时,需要用到global:whereis_name/1函数(关于local和global的区别和适用场合,可以参考erlang doc中的说明)。上面的停止服务器的机制依然正确。
因为游戏中很多应用和功能都是一个gen_server类型的服务器,在结束时需要做很多收尾工作,如状态的收集统计,结果存储和消息广播等等,我们可以在捕获到stop消息时来做这些工作,希望对刚接触erlang游戏开发的同学有一点点启发和帮助,这就是今天我目的。