erlang中gen_server/gen_fsm使用hibernate

来源:互联网 发布:网络推广平台找推网 编辑:程序博客网 时间:2024/05/17 01:10

简单的理解: 
hibernate可以使你闲置的erlang进程马上进行gc,因为进程处于receive状态下是不会gc的 
我在性能调优时发现,gc对于消息发送速度的影响还是非常大的 

关于erlang gc问题也可以参看这个 

引用

Recently, as part of RabbitMQ server development, we ran into an interesting issue regarding Erlang’s per-process garbage collection. If a process is idle — not doing any work at all, simply waiting for an external event — then its garbage-collector will not run until it starts working again. The solution is to hibernate idle processes, which causes a very aggressive garbage-collection run and puts the process into a suspended state, from which it will wake when a message next arrives. 


所以查看rabbitMQ源代码可以看到里面的gen_server/fsm等进程统统都会使用hibernate 

下面解释下如何让进程hibernate,这里需要注意的是一个进程hiberate后,会清空stack栈,也就是说之前的调用关系全部没有了,也就是说hibernate所在的函数永远不会返回,待有新消息时,进程从hibernate(M,F,A)里指定的M:F处开始运行 

gen_server和gen_fsm都提供了hibernate的方式,2种方法 

第一种: 
在gen_server,或gen_fsm回调接口返回时,指定hibernate 
Java代码  收藏代码
  1. {next_state, NStateName, NStateData, Time1}   

实际这个Time1可以指定为hibernate,则重新回到main loop时会直接hibernate 

这种方法可能使你的进程频繁hibernate又被唤醒,效率不是很好 

正确的方式应该是,当你预期你的进程一段时间内不会收到消息才hibernate 

所以推荐下面的方法,直接看下示例代码: 
Java代码  收藏代码
  1. %% ====================================================================  
  2. %% Interface Function  
  3. %% ====================================================================  
  4. join(UserId)->  
  5.     io:format("join UserId=~p~n",[UserId]),  
  6.     gen_fsm:sync_send_event(?MODULE, {join,UserId}).  
  7.   
  8. start()->  
  9.     gen_fsm:send_event(?MODULE,start).  
  10.   
  11. %% ====================================================================  
  12. %% Callback Function  
  13. %% ====================================================================  
  14. wait_player({join,UserId},_From, State) ->  
  15.     print_inner_data(State),  
  16.     put(p,get(p)+1),  
  17.     ets:insert(State#state.players,{p,UserId}),  
  18.     Count=State#state.count+1,  
  19.     if  
  20.         Count =:= 3 ->  
  21.             io:format("jump to next phrase -> start~n"),  
  22.             {reply,next_phrase,wait_start,State,5000};  
  23.         true->  
  24.             NewState=State#state{count=Count},  
  25.             {reply,stand_still,wait_player,NewState,5000}  
  26.     end.  
  27. wait_player(timeout,State)->  
  28.     io:format("timeout happened when wait_player,let's hibernate~n"),  
  29.     proc_lib:hibernate(gen_fsm, enter_loop, [?MODULE, [], wait_player,State]).  
  30.   
  31. wait_start(start,State)->  
  32.      print_inner_data(State),  
  33.      io:format("recv start event ~n"),  
  34.      {next_state,wait_start,State,5000};  
  35.   
  36. wait_start(timeout,State)->  
  37.     io:format("timeout happened when wait_start,let's hibernate~n"),  
  38.     proc_lib:hibernate(gen_fsm, enter_loop, [?MODULE, [], wait_start,State]).  
  39.   
  40. start_link()->  
  41.     gen_fsm:start_link({local,?MODULE}, ?MODULE, [] ,[]).  
  42.   
  43. init([])->  
  44.     put(p,0),  
  45.     {ok,   
  46.         wait_player,   
  47.         #state  
  48.         {  
  49.             count=0,  
  50.             players=ets:new(players,[bag])  
  51.         },  
  52.         5000  
  53.     }.  
  54.   
  55. print_inner_data(State)->  
  56.     io:format("inner data: dict=~p,ets=~p~n",[get(p),ets:tab2list(State#state.players)]).  


运行: 
Java代码  收藏代码
  1. 2> {ok,Pid}=fsm:start_link().  
  2. {ok,<0.39.0>}  
  3.   
  4. 3> process_info(Pid,total_heap_size).  
  5. {total_heap_size,233}  
  6.   
  7. 4> fsm:join(123).  
  8. join UserId=123  
  9. inner data: dict=0,ets=[]  
  10. stand_still  
  11. timeout happened when wait_player,let's hibernate  
  12.   
  13. 5> process_info(Pid,total_heap_size).  
  14. {total_heap_size,33}  
  15.   
  16. 6> fsm:join(456).  
  17. join UserId=456  
  18. inner data: dict=1,ets=[{p,123}]  
  19. stand_still  


总体上是让进程在main_loop里先timeout也就是证明一段时间内都没有消息到达,然后gen_fsm进程是发送{'gen_event',timeout}消息处理timeout事件的,所以要在每个Mod:StateName处都加个处理timeout的分支,来进行timeout处理,也就是hibernate掉自己,重新进入的是enter_loop 函数,这个东西很有用呐,大家可以查看源码,我就不多说了 

另外,示例中可以看到,hibernate会清空stack,但是不会影响你的进程字典和ets等其它东西,可以放心用了 


转载地址:http://bachmozart.iteye.com/blog/547934



0 0
原创粉丝点击