zoukankan      html  css  js  c++  java
  • OTP gen_server

     

    erlang behaviour小结之gen_server OTP入门

    分类: Erlang

    首先清楚gen_server提供C/S架构中的服务端的实现,即定义了自己一套规范的服务器框架。  

                                         

    在以上基础上,具体学习下gen_server的实现过程。

    先是定义模块的行为模式为gen_server

    [plain] view plaincopy
     
     
    1. -module(lqg).  
    2. -behaviour(gen_server).  
    3.   .  
    [plain] view plaincopy
     
     
    1. -module(lqg).  
    2. -behaviour(gen_server).  
    3.   .  
    [plain] view plaincopy
     
    1. -module(lqg).  
    2. -behaviour(gen_server).  
    3.   .  
    这里既然用了gen_server框架,那必须要实现gen_server的方法:gen_server callbacks
    [plain] view plaincopy
     
     
    1. -export([init/1, handle_call/3, handle_cast/2, handle_info/2,  terminate/2, code_change/3]).  
    [plain] view plaincopy
     
     
    1. -export([init/1, handle_call/3, handle_cast/2, handle_info/2,  terminate/2, code_change/3]).  
    [plain] view plaincopy
     
    1. -export([init/1, handle_call/3, handle_cast/2, handle_info/2,  terminate/2, code_change/3]).  
    外加模块的对外调用函数:API
    [plain] view plaincopy
     
     
    1. -export([start_link/0]).  
    [plain] view plaincopy
     
     
    1. -export([start_link/0]).  
    [plain] view plaincopy
     
    1. -export([start_link/0]).  
    [plain] view plaincopy
     
     
    1. -export([alloc/0,free/1]).  
    [plain] view plaincopy
     
     
    1. -export([alloc/0,free/1]).  
    [plain] view plaincopy
     
    1. -export([alloc/0,free/1]).  

    接下来就是实现上面定义的函数:

    [plain] view plaincopy
     
     
    1. start_link() ->  
    2.       gen_server:start_link({local, lqg}, lqg, [], []).  
    [plain] view plaincopy
     
     
    1. start_link() ->  
    2.       gen_server:start_link({local, lqg}, lqg, [], []).  
    [plain] view plaincopy
     
    1. start_link() ->  
    2.       gen_server:start_link({local, lqg}, lqg, [], []).  

    对于start_link,
    第一个参数为创建一个名为Name的server,现在的情况为在gen_srever将在本地被注册为lqg
    第二个参数lqg, 则是回调模块的名字,也就是回调函数所放的那个模块。在这里,接口函数( start_link)和回调函数(init, handle_call 和 handle_cast)。一般来说这是好的编程实践,将代表同一个进程的代码包含在同一个模块中。
    第三个参数[], 这个值将被原封不动传递给回调函数init。在这里,init无须任何输入数据将忽略这个参数.
    第四个参数[],是参数的列表。
    这里要注意的是:
    1.gen_server:start_link 是同步的。只有等到gen_server被完全初始化并准备接受请求之后才会返回
    2.如果gen_server是某棵监督树的一部分,即gen_server是由一个督程启动的,那么必须使用
    gen_server:start_link 。还有另外一个函数 gen_server:start 用于启动一个独立的gen_server,即不是某棵监督树一部分的一个gen_server

    [plain] view plaincopy
     
     
    1. init(_Args) ->  
    2.       {ok, channels()}.  
    [plain] view plaincopy
     
     
    1. init(_Args) ->  
    2.       {ok, channels()}.  
    [plain] view plaincopy
     
    1. init(_Args) ->  
    2.       {ok, channels()}.  

    在注册名称成功后,新的gen_server进程会调用回调函数lqg:init([])
    init返回{ok, State} ,其中State 是gen_server的内部状态。在这里,状态就是可用的频道channels。

    [plain] view plaincopy
     
     
    1. alloc() ->  
    2.      gen_server:call(lqg,alloc).  
    [plain] view plaincopy
     
     
    1. alloc() ->  
    2.      gen_server:call(lqg,alloc).  
    [plain] view plaincopy
     
    1. alloc() ->  
    2.      gen_server:call(lqg,alloc).  

    同步请求alloc() 用gen_server:call/2 实现.lqg 是gen_server的名字,必须和启动时的名字一样。alloc 是实际的请求
    此时,请求以消息的形式发送给这个gen_server。当收到了请求之后,gen_server调用handle_call(Request,From, State) ,它应返回一个元组 {reply, Reply, State1}。Reply是需要回馈给客户端的答复,同时State1 是gen_server的状态的新值。

    [plain] view plaincopy
     
     
    1. handle_call(_Request, _From, State) ->    
    2.     {Ch, State2} = alloc(State),   
    3.     {reply, Ch, State2}。  
    [plain] view plaincopy
     
     
    1. handle_call(_Request, _From, State) ->    
    2.     {Ch, State2} = alloc(State),   
    3.     {reply, Ch, State2}。  
    [plain] view plaincopy
     
    1. handle_call(_Request, _From, State) ->    
    2.     {Ch, State2} = alloc(State),   
    3.     {reply, Ch, State2}。  

    在这里,应答是分配了的频道Ch 然后gen_server将等待新的请求,并且现在保持了一个最新的可用频道的列表。

    [plain] view plaincopy
     
     
    1. handle_cast({free,Ch},Chs) ->  
    2.      Chs2 = free(Ch,Chs),  
    3.     {noreply, Chs2}.  
    [plain] view plaincopy
     
     
    1. handle_cast({free,Ch},Chs) ->  
    2.      Chs2 = free(Ch,Chs),  
    3.     {noreply, Chs2}.  
    [plain] view plaincopy
     
    1. handle_cast({free,Ch},Chs) ->  
    2.      Chs2 = free(Ch,Chs),  
    3.     {noreply, Chs2}.  

    在这里,新的状态便是更新过的可用频道列表Chs2 。gen_server现在又可以接受新的请求了。

    [plain] view plaincopy
     
     
    1. free(Ch) ->  
    2.      gen_server:cast(lqg,{free,Ch}).  
    [plain] view plaincopy
     
     
    1. free(Ch) ->  
    2.      gen_server:cast(lqg,{free,Ch}).  
    [plain] view plaincopy
     
    1. free(Ch) ->  
    2.      gen_server:cast(lqg,{free,Ch}).  

    异步请求free(ch) 使用 gen_server:cast/2 实现
    lqg 是gen_server的名称。{free, Ch} 是实际的请求。
    请求被装在一个消息中发给gen_server的cast ,这调用了 free ,然后返回了 ok 。当gen_server收到请求之后,它会调用handle_cast(Request, Stats) ,会返回一个元组{noreply, State1} 。 State1 是gen_server状态的新值。

    [plain] view plaincopy
     
     
    1. handle_info(_Info, State) ->    
    2.     {noreply, State}.  
    [plain] view plaincopy
     
     
    1. handle_info(_Info, State) ->    
    2.     {noreply, State}.  
    [plain] view plaincopy
     
    1. handle_info(_Info, State) ->    
    2.     {noreply, State}.  

    用来处理请求之外的信息。

    [plain] view plaincopy
     
     
    1. terminate(_Reason, _State) ->    
    2.     ok.  
    [plain] view plaincopy
     
     
    1. terminate(_Reason, _State) ->    
    2.     ok.  
    [plain] view plaincopy
     
    1. terminate(_Reason, _State) ->    
    2.     ok.  

    终止函数,终止正在运行的进程

    若gen_server是某个监督树的一部分,则无需停止函数。它的督程会自动终止它;

    如果gen_server并非某个监督树的一部分,那么可以用一个停止函数;
    如果在终止之前需要进行一些清理工作,那么关闭策略必须是一个超时值,同时gen_server必须在init 函数中设置为捕获退出信号。当gen_server被要求关闭时,它就会调用回调函数terminate(shutdown, State)。



    [plain] view plaincopy
     
     
    1. code_change(_OldVsn, State, _Extra) ->    
    2.     {ok, State}.  
    [plain] view plaincopy
     
     
    1. code_change(_OldVsn, State, _Extra) ->    
    2.     {ok, State}.  
    [plain] view plaincopy
     
    1. code_change(_OldVsn, State, _Extra) ->    
    2.     {ok, State}.  

    正如其名,此函数用来进行代码版本替换。是server热部署或代码升级时做callback修改进程状态
    _OldVsn:旧版本 State:gen_server的内部状态 _Extra:原封不动的传递过来的更新指令
    如果更新成功,返回{ok,State2},如果失败返回{error,Reason},并回滚到旧版本。

  • 相关阅读:
    Linux 服务器 个人常用操作命令记录
    Thinkphp5.0 自定义命令command的使用
    vue初学之node.js安装、cnpm安装、vue初体验
    php实现在不同国家显示网站的不同语言版本
    array_map、array_walk、array_filter三个函数的区别
    实现简单点赞功能
    SQL语言-----数据操作
    SQL语言
    MySQL高可用架构之Keepalived+主从架构部署
    MyCAT源码分析——分析环境部署
  • 原文地址:https://www.cnblogs.com/weidongprefer/p/6425691.html
Copyright © 2011-2022 走看看