gen_server
& 使用module:
& 使用场景:
1, gen_server behavior 通常用在负责资源分配的进程, client process 请求server process将会获得相应的资源;
2, 作为long-lived process 角色用于维护特定的资源, 如DB connections, ets table etc;
1, server 负责维护资源和提供接口
2, client 通过server 提供的接口访问资源
notice:
1, server 和 client 都属于Erlang process
2, 接口调用通过Erlang message 实现
gen_server 概述(X)
NB! 文档
gen_server:start_link 接口定义
init/1 callback 定义
%% interface start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). %% callback init([]) -> {ok, #state{}, ?HIBERNATE_TIMEOUT}.
NB! 文档
gen_server:call/2,3 接口定义
handle_call 几种返回形式
%% interface %% gen_server client need a new ets table new_ets() -> gen_server:call(?SERVER, create_ets_table). %% callback %% gen_server server process will execute new op and keep the ets table alive handle_call(create_ets_table, _From, State) -> EtsTable = ets:new(ets_table, [public]), {reply, EtsTable, State, ?HIBERNATE_TIMEOUT};
%% interface %% gen_server client is very lazy, would not execute logic by itself check_method_valid(MethodName) -> gen_server:call(?SERVER, {if_method_valid, MethodName}). %% callback %% then gen_server server help client handle_call({if_method_valid, MethodName}, _From, State) -> case MethodName of 'call' -> {reply, true, State, ?HIBERNATE_TIMEOUT}; 'cast' -> {reply, true, State, ?HIBERNATE_TIMEOUT}; _ -> {reply, false, State, ?HIBERNATE_TIMEOUT} end;
%% interface %% gen_server client has no resource which server had, so has to call server push_msg(Target, Msg) when erlang:is_pid(Target) -> gen_server:cast(?SERVER, {push_msg, Target, Msg}). %% callback %% gen_server server do handle_cast({push_msg, Target, Msg}, State) -> erlang:send(Target, Msg), {noreply, State, ?HIBERNATE_TIMEOUT};
NB! 文档
gen_server:cast/2 接口定义
handle_cast 几种返回形式
gen_server 概述(X)
gen_server 实例(X)
http://www.cnblogs.com/--00/tag/gen_server/
start(GenMod, LinkP, Name, Mod, Args, Options) GenMod :: gen_server | gen_fsm LinkP :: nolink | link Name :: {local, atom()} | {global, term()} | {via, atom(), term()} %% 指定的name Mod :: atom() %% 就是使用了gen_server behaviour 的模块名 Args :: term() %% Mod 模块 init func 的初始参数 Options :: [{timeout, Timeout} | {debug, [Flag]} | {spawn_opt, OptionList}] %% 这个参数是用来指定创建进程的参数 Flag :: trace | log | {logfile, File} | statistics | debug
其中start 是由gen module 下的init_it 函数调用 gen_server:init_it/6 触发start 的
init_it(Starter, self, Name, Mod, Args, Options) -> %% 注意, 如果使用nolink start 时, Parent 就是自己 init_it(Starter, self(), Name, Mod, Args, Options); init_it(Starter, Parent, Name0, Mod, Args, Options) -> Name = name(Name0), 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); …………
1, 便于维护资源
2, ...
1, 资源回收
2, 单进程瓶颈
In general, Erlang processes are expected toshort-lived and have small amounts of live data. When there is not enough free memory in the heap for a process, it is garbage collected, and if less memory can be freed than required it grows.Each process’ heap is garbage collected independently, when a process exits, its memory is simply reclaimed.
主要用于 long-lived 进程,最典型的就是gen_server 进程了.
short-lived 进程资源会随其退出轻松被回收.
通过整理进程的stack,回收进程的heap 来达到回收内存节省资源的效果
This sounds reasonable - let's try hibernating after every message and see what happens .
Note that emptying the call stack means that any surrounding catch is removed and has to be re-inserted after hibernation.
需要CPU , 可能带来不必要的empty-reinsert 操作.
Ejabberd 对hibernate 的使用比较谨慎, 只有在进程未收到任何信息一段时间后, 才使用hibernate .
handle_call(create_ets_table, _From, State) -> EtsTable = ets:new(ets_table, [public]), {reply, EtsTable, State, ?HIBERNATE_TIMEOUT};
handle_info(timeout, State) -> proc_lib:hibernate(gen_server, enter_loop, [?MODULE, [], State]), {noreply, State, ?HIBERNATE_TIMEOUT};
实现原理:
handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod) -> Result = try_handle_call(Mod, Msg, From, State), case Result of %% ... {ok, {reply, Reply, NState, Time1}} -> reply(From, Reply), loop(Parent, Name, NState, Mod, Time1, []); %% ... {ok, {noreply, NState, Time1}} -> loop(Parent, Name, NState, Mod, Time1, []) %% ... end;
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 版本中(gen_server2) 强制使用hibernate, 且与ejabberd的使用方式类似, 都是 在进程未收到任何信息 一段时间后 , 才使用hibernate .
init(Q) -> process_flag(trap_exit, true), ?store_proc_name(Q#amqqueue.name), {ok, init_state(Q#amqqueue{pid = self()}), hibernate, {backoff, ?HIBERNATE_AFTER_MIN, ?HIBERNATE_AFTER_MIN, ?DESIRED_HIBERNATE}, ?MODULE}.
gen_server 概述(X)
gen_server 实例(X)
gen_server 源码(X)
每个Erlang Process 默认 获得的资源是 相同 的
handle_call_call(Mod, Fun, Args, Gleader, To, S) -> RpcServer = self(), %% Spawn not to block the rpc server. {Caller,_} = erlang:spawn_monitor( fun () -> set_group_leader(Gleader), Reply = %% in case some sucker rex'es %% something that throws case catch apply(Mod, Fun, Args) of {'EXIT', _} = Exit -> {badrpc, Exit}; Result -> Result end, RpcServer ! {self(), {reply, Reply}} end), {noreply, gb_trees:insert(Caller, To, S)}.
handle client call via spawn a new process for temporary
handle_info({Caller, {reply, Reply}}, S) -> case gb_trees:lookup(Caller, S) of {value, To} -> receive {'DOWN', _, process, Caller, _} -> gen_server:reply(To, Reply), {noreply, gb_trees:delete(Caller, S)} end; none -> {noreply, S} end;
return the result to client by `gen_server:reply/2`
handle_call({spawn_opt,M,F,A,O,L,Gleader},{From,Tag},State) when is_pid(From) -> do_spawn([L,{From,Tag},M,F,A,Gleader],O,State); do_spawn(SpawnFuncArgs, SpawnOpts, State) -> [_,From|_] = SpawnFuncArgs, case catch spawn_opt(?MODULE, spawn_func, SpawnFuncArgs, SpawnOpts) of {'EXIT', {Reason,_}} -> async_reply({reply, {'EXIT', {Reason,[]}}, State}, From); {'EXIT', Reason} -> async_reply({reply, {'EXIT', {Reason,[]}}, State}, From); _ -> {noreply,State} end. spawn_func(link,{From,Tag},M,F,A,Gleader) -> link(From), gen_server:reply({From,Tag},self()), %% ahhh group_leader(Gleader,self()), apply(M,F,A); spawn_func(_,{From,Tag},M,F,A,Gleader) -> gen_server:reply({From,Tag},self()), %% ahhh group_leader(Gleader,self()), apply(M,F,A).
gen_server 概述(X)
gen_server 实例(X)
gen_server 源码(X)
gen_server 陷阱(X)
gen_server 概述(X)
gen_server 实例(X)
gen_server 源码(X)
gen_server 陷阱(X)
gen_server 技巧(X)