otp深入浅出之:gen_server

[size=large]what is the gen_server ?[/size]

man 中 gen_server定义是:
[quote]A behaviour module for implementing the server of a client-server relation. A generic server process (gen_server) implemented using this module will have a standard set of interface functions and include functionality for tracing and error reporting. It will also fit into an OTP supervision tree.[/quote]


[size=large]一个简单的echo gen_server[/size]

-module(echo).

-behaviour(gen_server).


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

-record(state, {}).

echo(Msg)->
gen_server:call(?MODULE,{echo , Msg} ).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init([]) ->
{ok, #state{}}.

handle_call({echo , Msg}, _From, State) ->
{reply , Msg , State };
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
%%其他自动生成部分省略


echo gen_server代码的主干可以由模板生成,具体请参google。


运行并测试echo gen_server

echo:start_link(),
echo:echo("hello world").


[size=large]how to start a echo gen_server ?[/size]
[img]http://uniseraph.iteye.com/upload/attachment/100505/14b66f0e-bdbb-3433-ad40-0c815b098f5f.png[/img]

如上图,在主进程中的调用顺序为:echo:start_link->gen_server:start_link->gen:start->proc_lib:start_link

proc_lib:start_link中通过erlang:spawn_link创建echo进程,新进程中依次回调
gen:init_it->gen_server:init_it->echo:init,从而注入echo自己的初始化逻辑。

在gen_server:init_it中分析回调echo:init的返回值,初始化成功以后则进入循环。

init_it(Starter, Parent, Name, Mod, Args, Options) ->
Debug = debug_options(Name, Options),
case catch Mod:init(Args) of
{ok, State} ->
proc_lib:init_ack(Starter, {ok, self()}),
loop(Parent, Name, State, Mod, infinity, Debug);
{ok, State, Timeout} ->
proc_lib:init_ack(Starter, {ok, self()}),
loop(Parent, Name, State, Mod, Timeout, Debug);
{stop, Reason} ->
proc_lib:init_ack(Starter, {error, Reason}),
exit(Reason);
ignore ->
proc_lib:init_ack(Starter, ignore),
exit(normal);
{'EXIT', Reason} ->
proc_lib:init_ack(Starter, {error, Reason}),
exit(Reason);
Else ->
Error = {bad_return_value, Else},
proc_lib:init_ack(Starter, {error, Error}),
exit(Error)
end.




[size=large]how to deal with a call ?[/size]

gen_server:call({local,echo},{echo,"hello,world"}).

gen_server:call调用gen:call发起一个同步的消息请求,使用标签"$gen_call"作为表示,表示这是应用的消息,不是系统的消息。

call(Name, Request) ->
case catch gen:call(Name, '$gen_call', Request) of
{ok,Res} ->
Res;
{'EXIT',Reason} ->
exit({Reason, {?MODULE, call, [Name, Request]}})
end.


在主循环gen_server:loop中从消息队列中依次取出消息并处理,注意这是串行的,[size=large]在一条消息处理完毕之前,不会处理下一条消息[/size]。

loop(Parent, Name, State, Mod, Time, Debug) ->
Msg = receive
Input ->
Input
after Time ->
timeout
end,
decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, false).



gen_server:decode_msg区分不同消息类型,分别处理

decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Hib) ->
case Msg of
{system, From, Req} ->
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
[Name, State, Mod, Time], Hib);
{'EXIT', Parent, Reason} ->
terminate(Reason, Name, Msg, Mod, State, Debug);
_Msg when Debug =:= [] ->
handle_msg(Msg, Parent, Name, State, Mod);
_Msg ->
Debug1 = sys:handle_debug(Debug, {?MODULE, print_event},
Name, {in, Msg}),
handle_msg(Msg, Parent, Name, State, Mod, Debug1)
end.


gen_server:handle_msg回调echo:handle_call处理应用信息,处理成功则返回loop方法。

handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod) ->
case catch Mod:handle_call(Msg, From, State) of
{reply, Reply, NState} ->
reply(From, Reply),
loop(Parent, Name, NState, Mod, infinity, []);
{reply, Reply, NState, Time1} ->
reply(From, Reply),
loop(Parent, Name, NState, Mod, Time1, []);
{noreply, NState} ->
loop(Parent, Name, NState, Mod, infinity, []);
{noreply, NState, Time1} ->
loop(Parent, Name, NState, Mod, Time1, []);
{stop, Reason, Reply, NState} ->
{'EXIT', R} =
(catch terminate(Reason, Name, Msg, Mod, NState, [])),
reply(From, Reply),
exit(R);
Other -> handle_common_reply(Other, Parent, Name, Msg, Mod, State)
end;



[size=large]how to deal with a cast ?[/size]

[size=large]how to deal with a info ?[/size]


要注意的地方
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值