erlang学习笔记之gen_server 入门

以下三点是编写gen_server回调模块的简要步骤:

(1) 确定回调模块名。

(2) 编写接口函数。

(3) 在回调模块里编写六个必需的回调函数。 

1.确定回调模块名

我们将制作一个简单的支付系统。把这个模块称为my_bank(我的银行)

2.编写接口方法

我们将定义五个接口方法,它们都在my_bank模块里。 

1.start() 打开银行

 2.stop() 关闭银行。 

3.new_account(Who) 创建一个新账户。  

4.deposit(Who, Amount) 把钱存入银行。

5.withdraw(Who, Amount) 把钱取出来(如果有结余的话)。

每个函数都正好对应一个gen_server方法调用,代码如下:

start() -> gen_server:start_link({local, ?SERVER}, ?MPDULE, [], []).
stop() -> gen_server:call(?MPDULE, stop).

new_account(who) -> gen_server:call(?MPDULE, {new, who}).
deposit(who, Amount) -> gen_server:call(?MPDULE, {add, who, Amount}).
withdraw(who, Amount) -> gen_server:call(?MPDULE, {remove, who, Amount}).

3.编写回调方法 

回调模块必须导出六个回调方法:

init/1、handle_call/3、handle_cast/2、 handle_info/2、terminate/2和code_change/3。

%% gen_server 迷你模板
-behaviour(gen_server).
-export([start_link/0]).
%% gen_server回调击数
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

init([State]) -> {ok, State}.

handle_call(_Request, _From, State) ->
  (reply, Reply, State).
handle_cast(_Msg, state) -> {noreply, state}.
handle_info(_Info, State) -> {noreply, State}.
terminate(_Reason, _State) -> ok.
code_change(_OldVsn, State, Extra) -> {ok, State}.

这个模板包含了一套简单的框架,可以填充它们来制作服务器。如果忘记定义合适的回调函数,编译器就会根据关键字-behaviour来生成警告或错误消息。start_link()函数里的服务器名(宏?SERVER)需要进行定义,默认是没有定义的

填写模板并稍加改动之后,就形成了以下代码:

%% API
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

init([]) -> {ok, ets:new(?MODULE, [])}.

handle_call({new, Who}, _From, Tab) ->
  Reply = case ets:lookup(Tab, who) of
            [] -> ets:insert(Tab, {who, 0}),
              {welcome, whot};
            [_] -> {Who, you_already_are_a_customer}
          end,
  {reply, Reply, Tab};

handle_call({add, Who, X}, _From, Tab) ->
  Reply = case ets:lookup(Tab, Who) of
            [] -> not_a_customer;
            [{Who, Balance}] ->
              NewBalance = Balance + X,
              ets:insert(Tab, {Who, NewBalance}),
              {thanks, Who, your_balance_is, NewBalance}
          end,
  {reply, Reply, Tab};

handle_call({remove, Who, X}, _From, Tab) ->
  Reply = case ets:lookup(Tab, who) of
            [] -> not_a_customer;
            [{Who, Balance}] when X =< Balance -> NewBalance = Balance - X,
              ets:insert(Tab, {who, NewBalance}),
              {thanks, Who, your_balance_is, NewBalance};
            [{who, Balance}] ->
              {sorry, Who, you_only_have, Balance, in_the_bank}
          end,
  {reply, Reply, Tab};

handle_call(stop, _From, Tab) ->
  {stop, normal, stopped, Tab}.

handle_cast(_Msg, State) -> {noreply,
  State}.

handle_info(_Info, State) ->
  {noreply, State}.

terminate(_Reason, _State) -> ok.

code_change(_0ldVsn, State, _Extra) ->
  {ok, State}.

 

调用gen_server:start_link(Name, CallBackMod, StartArgs, Opts)来启动服务器, 之后第一个被调用的回调模块方法是Mod:init(StartArgs),它必须返回{ok, State}。

State的值作为handle_call的第三个参数重新出现。

handle_call(stop, From, Tab)返回{stop, normal, stopped, Tab},它会停止服务器。

第二个参数(normal)被用作my_bank:terminate/2的首 个参数。第三个参数(stopped)会成为my_bank:stop()的返回值

你可能感兴趣的:(erlang,学习,笔记)