erlang最常見的組件gen_server

相信用過erlang的同學(xué)對gen_server并不陌生笛谦,我們在日常使用中,和gen_server打交道的次數(shù)也是最多的当凡。至于用法我這邊也不會詳細(xì)的說明儒老,只是略微帶過蝴乔,我會將篇幅用在更加不常用但是卻很有用的功能上。

1.一般用法以及原理

call(Name,Request,Timeout)實際是向目標(biāo)proc發(fā)送了{'$gen_call',{self(), Mref}, Request}}的消息

cast(Name,Request)實際是向目標(biāo)proc發(fā)送了{'$gen_cast',{self(), Mref}, Request}}的消息

這些只不過是gen做的更通用語法糖而已call(Process, Label, Request)

在處理消息的時候驮樊,并沒有先后順序,而是從message box依次取出消息進行處理片酝,遇到{'$gen_call',{self(), Mref}, Request}}這類消息囚衔,丟給回調(diào)模塊的handle_call處理,遇到{'$gen_cast',{self(), Mref}, Request}}這類消息丟給回調(diào)模塊的handle_cast處理雕沿,剩下的不能識別的消息交給回調(diào)模塊的handle_info處理练湿,當(dāng)然這里有一個前提,不是系統(tǒng)消息审轮,比如suspend,resume等肥哎,這類消息是另外的處理邏輯,這里代碼我就不貼了疾渣。

2.合理的使用timeout

2.1 使用timeout可以做超時工作(timer,TTL)

使用場景篡诽,例如timer.erl的定時任務(wù)
handle_call的返回值中返回{reply,Reply,NewState,Timeout}可以在Timeout時間內(nèi)收不到消息的情況下,自己主動收到一個timeout的消息,timer就是根據(jù)這一原理打造的榴捡,從而觸發(fā)想要執(zhí)行的MFA
但是同時有一個隱藏的問題存在杈女,那就是系統(tǒng)消息,在處理系統(tǒng)消息之后吊圾,它并沒有修改超時時間达椰,導(dǎo)致收到timeout消息滯后,舉個例子项乒,如何來讓timer出現(xiàn)上述問題啰劲。之前項目組使用的第三方crontab插件(https://github.com/b3rnie/crontab)也會有這個問題。如果要克服這個問題檀何,不能以gen_server為組件蝇裤。

Eshell V10.2  (abort with ^G)
1> G = fun()->
1>   F = fun() -> receive Msg -> io:format("~p rev ~p ~n", [erlang:localtime(), Msg]) end end,
1>   P = erlang:spawn(F),
1>   timer:start(),
1>   io:format("now ~p ~n",[erlang:localtime()]),
1>   timer:send_after(1000 * 5, P, {msg, erlang:localtime()}),
1>   sys:suspend(timer_server),
1>   receive
1>   after 1000 * 7 -> ok
1>   end,
1>   sys:resume(timer_server) end.
#Fun<erl_eval.20.128620087>
2> G().
now {{2019,11,21},{23,5,25}} 
ok
% 按道理會打印出 {{2019,11,21},{23,5,30}} rev ....廷支,但是由于timer_server被掛起了,所以延后了猖辫。
{{2019,11,21},{23,5,37}} rev {msg,{{2019,11,21},{23,5,25}}} 
3> 

源代碼分析:

decode_msg(Msg, Parent, Name, State, Mod, Time, HibernateAfterTimeout, Debug, Hib) ->
    case Msg of
    {system, From, Req} ->
        sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
        % 問題出在這里K峙ⅰ!啃憎!這里的Time應(yīng)該減去操作用的時間
        [Name, State, Mod, Time, HibernateAfterTimeout], Hib);
    {'EXIT', Parent, Reason} ->
        terminate(Reason, ?STACKTRACE(), Name, undefined, Msg, Mod, State, Debug);
    _Msg when Debug =:= [] ->
        handle_msg(Msg, Parent, Name, State, Mod, HibernateAfterTimeout);
    _Msg ->
        Debug1 = sys:handle_debug(Debug, fun print_event/3,
                      Name, {in, Msg}),
        handle_msg(Msg, Parent, Name, State, Mod, HibernateAfterTimeout, Debug1)
    end.
2.2 hibernate可以優(yōu)化內(nèi)存

如果可以預(yù)見在短時間內(nèi)沒有消息到達(dá)芝囤,可以讓gen_server休眠(hibernate),可以大大的節(jié)省內(nèi)存開支辛萍,為此我專門寫了一個測試代碼(https://github.com/aijingsun6/erl_hibernate.git),兩種情況都生成10萬個gen_server悯姊,一種情況使用hibernate,另一種情況不使用hibernate,測試結(jié)果如下:

% 使用hibernate
rebar.config
{erl_opts, [
  {d, hibernate},
  debug_info
]}.

rebar compile
werl -pa ebin -P 300000 -s erl_hibernate
1> erlang:memory().

[{total,209592416},
 {processes,170086224},
 {processes_used,170085280},
 {system,18446744073749057808},
 {atom,3699033},
 {atom_used,3694166},
 {binary,692816},
 {code,4682355},
 {ets,351424}]
 
 % 不使用hibernate
 rebar.config
{erl_opts, [
  %{d, hibernate},
  debug_info
]}.

rebar compile
werl -pa ebin -P 300000 -s erl_hibernate
1> erlang:memory().

[{total,369103040},
 {processes,330034704},
 {processes_used,330033760},
 {system,39068336},
 {atom,3404049},
 {atom_used,3393117},
 {binary,624272},
 {code,4615561},
 {ets,350464}]

可見在使用hibernate的情況下贩毕,內(nèi)存大幅減少

3. debug大有用途

trace 可以打印每一條Msg
log 可以捕獲倒數(shù)N條Msg
log_to_file 可以將Msg輸出到文本,特別適用線上追溯問題
statistics 可以統(tǒng)計一段時間的reductions,消息的進(in)出(out)數(shù)量

總結(jié)

正確理解gen_server不僅讓你寫出高效的服務(wù)悯许,而且還會避免不必要的麻煩。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末辉阶,一起剝皮案震驚了整個濱河市先壕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌谆甜,老刑警劉巖垃僚,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異规辱,居然都是意外死亡谆棺,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門罕袋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來改淑,“玉大人,你說我怎么就攤上這事浴讯《湎模” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵兰珍,是天一觀的道長侍郭。 經(jīng)常有香客問我,道長掠河,這世上最難降的妖魔是什么亮元? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮唠摹,結(jié)果婚禮上爆捞,老公的妹妹穿的比我還像新娘。我一直安慰自己勾拉,他們只是感情好煮甥,可當(dāng)我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布盗温。 她就那樣靜靜地躺著,像睡著了一般成肘。 火紅的嫁衣襯著肌膚如雪卖局。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天双霍,我揣著相機與錄音砚偶,去河邊找鬼。 笑死洒闸,一個胖子當(dāng)著我的面吹牛染坯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播丘逸,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼单鹿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了深纲?” 一聲冷哼從身側(cè)響起仲锄,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎湃鹊,沒想到半個月后昼窗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡涛舍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了唆途。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片富雅。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖肛搬,靈堂內(nèi)的尸體忽然破棺而出没佑,到底是詐尸還是另有隱情,我是刑警寧澤温赔,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響回溺,放射性物質(zhì)發(fā)生泄漏茫打。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一拜秧、第九天 我趴在偏房一處隱蔽的房頂上張望痹屹。 院中可真熱鬧,春花似錦枉氮、人聲如沸志衍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽楼肪。三九已至培廓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間春叫,已是汗流浹背肩钠。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留象缀,地道東北人蔬将。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像央星,于是被迫代替她去往敵國和親霞怀。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容