注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Code@Pig Home

喜欢背着一袋Code傻笑的Pig .. 忧美.欢笑.记忆.忘却 .之. 角落

 
 
 

日志

 
 

[每日一Errr~lang] OTP (1) ---- gen_server  

2010-03-03 21:50:00|  分类: lang_erlang |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
通过上一期节目的介绍,我们已经可以用 erlang 写出 fault-tolerance 的程序了。如何做呢?比如 A, B 进程相互 link 着。A 负责监控 B,B 负责处理业务,B 挂了,可以由 A 负责将其 reboot。

对于 erlang,肯定有一些使用惯例,类似于某种 framework,让你照着用,就可以解决很多实际问题了。这就是 OTP (Open Telecom Platform),一个庞大的类库,提供了各种工具,同时定义了一些惯例。

今后几期的节目,都将介绍《OTP Design Principle》,算是其的 lite 版本吧。
原版:http://www.erlang.org/doc/design_principles/des_princ.html
中文:http://erlang.shiningray.cn/otp-design-principles/

今天我们来说说 gen_server。其中 gen_ 表示 generic。

gen_server 的使用
国际惯例,先上代码:
----------------------------------------
-module(myserver).
-behaviour(gen_server).

-export([init/1, handle_call/3, handle_cast/2, terminate/2, code_change/3]).
-export([start_link/0, add/2, find/1, remove/1]).

% start_link param, ServerName, Module, Args, Options
start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

add(User, Desc) -> gen_server:call(?MODULE, {add, User, Desc}).
find(User) -> gen_server:call(?MODULE, {find, User}).
remove(User) -> gen_server:cast(?MODULE, {remove, User}).

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

handle_call({add, User, Desc}, _From, State) ->
    NewState = [{User, Desc}|State],
    {reply, ok, NewState};

handle_call({find, User}, _From, State) ->
    Desc = find_desc(User, State),
    {reply, Desc, State}.

handle_cast({remove, User}, State) ->
    NewState = remove_user(User, State),
    {noreply, NewState}.

%handle_info(_, State) ->
%    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

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


%
% find name
%

find_desc(_FindUser, []) ->
    not_found;
find_desc(FindUser, [{FindUser, Desc}|_T]) ->
    Desc;
find_desc(FindUser, [_H|T]) ->
    find_desc(FindUser, T).


%
% remove user
%
remove_user(_FindUser, []) ->
    [];
remove_user(FindUser, [{FindUser, _Desc}|T]) ->
    T;
remove_user(FindUser, [H|T]) ->
    Rest = remove_user(FindUser, T),
    [H|Rest].
----------------------------------------
1> c(myserver).
./myserver.erl:2: Warning: undefined callback function handle_info/2 (behaviour 'gen_server')
{ok,myserver}
2> myserver:start_link().
{ok,<0.39.0>}
3> myserver:add(kasicass, "a big guy").
ok
4> myserver:add(ylhu, "water dragon").
ok
5> myserver:find(kasicass).
"a big guy"
6> myserver:remove(kasicass).
ok
7> myserver:find(kasicass).
not_found
----------------------------------------

为啥会有 gen_server?因为很多模块,其实都是 singleton 的,只有一份实例(数据)。gen_server 就用于这种情况,比如我们需要一个 UserManager,把 socket 与程序内部的 User 结构对应起来。

erlang 中的 -behaviour(gen_server). 告诉编译器,此模块是某个 berhaviour 的具体业务逻辑,说明此模块就是 EJB 你自己写的 SessionBean。实际上面的代码跑起来,调用结构如下:

myserver(1) ==> gen_server ==> myserver(2)

myserver(1) 就是 -export([start_link/0, add/2, find/1, remove/1]). 这一堆接口,相当于一个 proxy。
myserver(2) 就是 -export([init/1, handle_call/3, handle_cast/2, terminate/2, code_change/3]). 一堆接口,逻辑跑在一个独立 process 中,此 process 由 gen_server:start_link 创建。


gen_server 要求实现的接口

init(Args) -> Result
myserver(2) 启动时调用,让模块内部初始化数据。其中 Args 就是 start_link 的第三个参数。
返回值 {ok, State} 中的 State 就是这个 myserver(2) 使用的全局数据。

handle_call(Request, From, State) -> Result
同步调用,调用者会 block,等待返回值。{reply, Reply, NewState},其中 Reply 就是给调用者的返回值。

handle_cast(Request, State) -> Result
异步调用,调用者立即返回。{ noreply, NewState }

handle_info(Info, State) -> Result
当 myserver(2) 进程受到消息,不能通过 handle_call, handle_cast 处理时,则在 handle_info/2 处理。一般没有特殊情况,可以不定义此函数。则 gen_server 在受到错误消息时,会有警告,也方便代码调试。

terminate(Reason, State)
与 init/1 相反。在 myserver(2) 进程退出时,调用此函数,做一些清理工作。

code_change(OldVsn, State, Extra) -> {ok, NewState}
哈,这个就是所谓的“代码热部署”啦。如果代码切换会带来数据结构上的调整,可以在这里做数据转换的工作。具体如何时候,我们以后的节目会详细介绍。


gen_server 多说两句

因为 erlang 的所有数据都是参数传递的,如果没有 gen_server 帮我们管理 myserver(2) 的 State。
则接口就会变成这样:
myserver:add(User, Name, State)
myserver:remove(User, State)
调用者还需要自己拿着这些 State 传来传去,恩,麻烦~~

也因为同样的理由,我们写 handle_call, handle_cast 这些函数的时候,都需要把 NewState 作为返回值,传递给 gen_server 框架。

ok~ 本期节目又结束了,欢迎下次观看。最后,让我们吼吼节目口号:"信erlang,得永生,原地慢血复活。O Yeah~"。
  评论这张
 
阅读(936)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017