一个gen_server服务器在运行周期里面保持了一系列运行状态,erlang根据运行状态来决定是否停止该服务,今天说的是常规方法停服务的方法,至于由异常引起的服务停止,又要分为我们有没有提前做过trap_exit处理,改日再说。

   先看一个简单的服务器,什么也不处理,只是启动起来。

   
   
   
   
  1. -module(lab). 
  2. -author('[email protected]'). 
  3. -compile(export_all). 
  4. -behaviour(gen_server). 
  5. -export([start_link/0]). 
  6. -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). 
  7. -record(state, {}). 
  8.  
  9. start_link() -> 
  10.     gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). 
  11.  
  12. init([]) -> 
  13.     State = #state{}, 
  14.     ?DEBUG("~w runing up", [?MODULE]), 
  15.     {ok, State}. 
  16.  
  17. handle_call(_Request, _From, State) -> 
  18.     {noreply, State}. 
  19.  
  20. handle_cast(_Msg, State) -> 
  21.     {noreply, State}. 
  22.  
  23. handle_info(_Info, State) -> 
  24.     {noreply, State}. 
  25.  
  26. terminate(_Reason, _State) -> 
  27.     ok. 
  28.  
  29. code_change(_OldVsn, State, _Extra) -> 
  30.     {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游戏开发的同学有一点点启发和帮助,这就是今天我目的。