• [转]理解gen_server behaviour


    在Erlang/OTP中有一个基本概念叫监督树。这是一种建立在督程佣程思想上的进程结构化模型。
    • 佣程(worker)是进行计算的进程,也就是说,它们进行实际的工作。
    • 督程(supervisor)是监视工作者行为的进程。监督者可以重启工作者如果出现了什么问题.
    • 监督树是一种将代码分成监督者和工作者的层次安排,这样才能设计和编写可容错的软件。
    _images/sup6.gif

    上图中,方框提供监督,圆圈是工作者。

    行为

    在监督树中,很多进程有着相似结构,遵循类似的模式。例如,督程的结构都很相 似。他们之间的唯一区别在于所监督的子进程。此外,很多佣程都是处于服务器-客户端关系中的服务器,有限状态机或者诸如错误日志这样的事件处理器。

    行为是对这些常见模式的形式化。其思想是将一个进程的代码划分为一个通用的部分(行为模块)和一个特定的部分(回调模块)。

    行为模块是Erlang/OTP的一部分。要实现一个督程,用户只需要实现回调模块,导出预定义集合中的函数—— 回调函数

    一个例子可以用来说明代码是如何被划分成为通用和特定部分的:考虑下面的代码(普通Erlang编写),一个简单的服务器,用于保持跟踪一些“频道”。其他进程可以通过调用 alloc/0free/1 函数来相应地分配和释放一个频道。

    %%ch2.erl:

    -module(ch2).
    -export([start/0]).
    -export([alloc/0, free/1]).
    -export([init/0, handle_call/2, handle_cast/2]).
    
    start() ->
        server:start(ch2).
    
    alloc() ->
        server:call(ch2, alloc).
    
    free(Ch) ->
        server:cast(ch2, {free, Ch}).
    
    init() ->
        channels().
    
    handle_call(alloc, Chs) ->
        alloc(Chs). % => {Ch,Chs2}
    
    handle_cast({free, Ch}, Chs) ->
        free(Ch, Chs). % => Chs2
    
     
    channels() ->
       {_Allocated = [], _Free = lists:seq(1,100)}.
    
    alloc({Allocated, [H|T] = _Free}) ->
       {H, {[H|Allocated], T}}.
    
    free(Ch, {Alloc, Free} = Channels) ->
       case lists:member(Ch, Alloc) of
          true ->
             {lists:delete(Ch, Alloc), [Ch|Free]};
          false ->
             Channels
       end.
    

    服务器的代码可以重写为一个通用的部分 server.erl:

    -module(server).
    -export([start/1]).
    -export([call/2, cast/2]).
    -export([init/1]).
    start(Mod) ->
        spawn(server, init, [Mod]).
    call(Name, Req) ->
        Name ! {call, self(), Req},
        receive
            {Name, Res} ->
                Res
        end.
    cast(Name, Req) ->
        Name ! {cast, Req},
        ok.
    init(Mod) ->
        register(Mod, self()),
        State = Mod:init(),
        loop(Mod, State).
    loop(Mod, State) ->
        receive
            {call, From, Req} ->
                {Res, State2} = Mod:handle_call(Req, State),
                From ! {Mod, Res},
                loop(Mod, State2);
            {cast, Req} ->
                State2 = Mod:handle_cast(Req, State),
                loop(Mod, State2)
        end.
    
    注意以下几点:
    • server 中的代码可以被重用于建立很多不同的服务器端。
    • 服务器的名字——这个例子中为原子 ch2——对于客户端函数的用户而言是隐藏的。这意味无须影响客户端就可以改变名字。
    • 协议(发给服务器和从服务器接收到的消息)也是隐藏的。这是很好的编程实践,让我们可以在不改变接口函数的代码的情况下改变协议。
    • 我们可以扩展服务器 server 的功能,而不用改变 ch2 或任何其它的回调模块。

     ---------------------------------------------------------------

    这个服务器可以用 gen_server 进行重写,结果产生这个回调模块:

    -module(ch3).
    -behaviour(gen_server).

    -export([start_link/0]).
    -export([alloc/0, free/1]).
    -export([init/1, handle_call/3, handle_cast/2]).

    start_link() ->
        gen_server:start_link({local, ch3}, ch3, [], []).

    alloc() ->
        gen_server:call(ch3, alloc).

    free(Ch) ->
        gen_server:cast(ch3, {free, Ch}).

    init(_Args) ->
        {ok, channels()}.

    handle_call(alloc, _From, Chs) ->
        {Ch, Chs2} = alloc(Chs),
        {reply, Ch, Chs2}.

    handle_cast({free, Ch}, Chs) ->
        Chs2 = free(Ch, Chs),
        {noreply, Chs2}.
    -------------------------------------------------------------------

    gen_server 的交互序列

    1. 进程如何启动
    2. 如何处理同步请求 Synchronous Requests - Call
    3. 如何处理异步请求 Asynchronous Requests - Cast
    4. 通用消息处理 handle_info
    5. 如何处理进程终止
    6. 如何进行代码版本替换
      gen_server module            Callback module
      -----------------                       
      ---------------
      gen_server:start_link -----> Module:init/1
      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   
     
  • 相关阅读:
    5G 需要换 SIM 卡吗?
    多线程设计模式
    4G LTE/EPC UE 的附着与去附着
    4G EPS 的网络协议栈
    MYSQL远程登录权限设置 ,可以让Navicat远程连接服务器的数据库
    MYSQL远程登录权限设置 ,可以让Navicat远程连接服务器的数据库
    MYSQL远程登录权限设置 ,可以让Navicat远程连接服务器的数据库
    2018年要学习的10大Python Web框架
    2018年要学习的10大Python Web框架
    2018年要学习的10大Python Web框架
  • 原文地址:https://www.cnblogs.com/freebird92/p/2302312.html
Copyright © 2020-2023  润新知