1. 程式人生 > >系統技術非業餘研究 » Erlang supervisor規格的dynamic行為分析

系統技術非業餘研究 » Erlang supervisor規格的dynamic行為分析

今天benjamin同學在網上問了以下問題:

我在看mochiweb和misultin的程式碼時有一些不理解的地方,以下是程式碼:

init({MainSupRef, Port, OptionsTcp, AcceptorsPoolsize, RecvTimeout, SocketMode, CustomOpts}) ->
?LOG_DEBUG(“starting listening ~p socket with options ~p on port ~p”, [SocketMode, OptionsTcp, Port]),
case misultin_socket:listen(Port, OptionsTcp, SocketMode) of
{ok, ListenSocket} ->
Acceptors = [
{{acceptor, N}, {misultin_acceptor, start_link, [MainSupRef, ListenSocket, Port, RecvTimeout, SocketMode, CustomOpts]},
permanent, brutal_kill, worker, dynamic}
|| N ],
{ok, {{one_for_one, 5, 10}, Acceptors}};

我不明白的就是為什麼是dynamic,我檢視supervisor文件,文件上寫只有gen_event才應該是dynamic,而這裡misultin_acceptor這個程序是使用proc_lib:spawn_link產生的。我在proc_lib的文件中也沒有發現這裡為什麼應該使用dynamic。請您指教。

考慮到這種應用很多,而且基本上supervisor文件上講的不是很清楚,所以我花時間調查了下,以下是我調查的過程和結果:

我們從 supervisor的 文件開始,可以看到supervisor規格書的定義:

This is the type definition of a child specification:
child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
Id = term()
StartFunc = {M,F,A}
M = F = atom()
A = [term()]
Restart = permanent | transient | temporary
Shutdown = brutal_kill | int()>=0 | infinity
Type = worker | supervisor
Modules = [Module] | dynamic
Module = atom()

其中規格里面module部分可以定義為dynamic的只有gen_event,看文件:

Modules is used by the release handler during code replacement to determine which processes are using a certain module. As a rule of thumb Modules should be a list with one element [Module], where Module is the callback module, if the child process is a supervisor, gen_server or gen_fsm. If the child process is an event manager (gen_event) with a dynamic set of callback modules, Modules should be dynamic. See OTP Design Principles for more information about release handling.

Modules should be a list with one element [Module], where Module is the name of the callback module, if the child process is a supervisor, gen_server or gen_fsm. If the child process is a gen_event, Modules should be dynamic.

那麼為什麼mochiweb和misultin要定義成dynamic呢?

在正常情況下,被監督的程序如果死掉了,supervisor會根據規格重新啟動程序或者做出相應的行動。 但是翻看了supervisor.erl所有的程式碼也沒發現dynamic有什麼特殊作用: one_for_one模式下permanent型別的程序出錯,supervisor只是負責重新啟動,其他的也沒做啥。

起先我想不通,但是後來我想到了這個資訊可能和release handler的程式碼熱升級有關係。 在熱升級的時候,針對supervisor管理的程序,需要知道它是由那個模組apply執行的。 這樣才能在模組程式碼發生變更的時候,好判斷出該程序是否要做點升級的準備。 所以規格書不僅僅是給supervisor模組用的,也給release handler模組用。其中的modules部分就是描述那些模組和這個程序有關係。 對於gen_server, gen_fsm behaviour的模組來講, 它的程序由只有它自己spawn_link來的,所以很好理解填規格的時候,模組部分填他自己就好。 但是gen_event這樣的模組,由於一個事件通常會註冊好幾個模組,而且是動態的,所以規格書就不知道填什麼,只好填dynamic. 在熱升級需要知道模組的時候,即刻發訊息現查詢那些模組和這個程序相關。

有了這個理論基礎後,我們來看下release_handler_1.erl:510

%%-----------------------------------------------------------------
%% Func: get_supervised_procs/0
%% Purpose: This is the magic function.  It finds all process in
%%          the system and which modules they execute as a call_back or
%%          process module.
%%          This is achieved by asking the main supervisor for the
%%          applications for all children and their modules
%%          (recursively).
%% NOTE: If a supervisor is suspended, it isn't possible to call
%%       which_children.  Code change on a supervisor should be
%%       done in another way; the only code in a supervisor is
%%       code for starting children.  Therefore, to change a
%%       supervisor module, we should load the new version, and then
%%       delete the old.  Then we should perform the start changes
%%       manually, by adding/deleting children.
%% Returns: [{SuperPid, ChildName, ChildPid, Mods}]
%%-----------------------------------------------------------------
%% OTP-3452. For each application the first item contains the pid
%% of the top supervisor, and the name of the supervisor call-back module.  
%%-----------------------------------------------------------------
...
get_supervised_procs() ->
...
get_procs(_, _Sup) ->
    [].

get_dynamic_mods(Pid) ->
    {ok,Res} = gen:call(Pid, self(), get_modules),
    Res.

我們可以看到 release_handler會給透過gen模組給程序傳送get_modules訊息來獲取相應的模組資訊的, 這個訊息就是 {From, Tag, get_modules} 。

我們來看下gen_event.erl:277是如何處理這個訊息的:

...
  {From, Tag, get_modules} ->
            ?reply(get_modules(MSL)),
            loop(Parent, ServerName, MSL, Debug, false);
...

%% Message from the release_handler.
%% The list of modules got to be a set !
get_modules(MSL) ->
    Mods = [Handler#handler.module || Handler <- MSL],
    ordsets:to_list(ordsets:from_list(Mods)).

到這裡為止,我想大家應該明白了,整個處理流程是如何的,訊息是如何流動的。

那我們再回到mochiweb,結合我們的問題來驗證下。 先看下 mochiweb_socket_server.erl:294:

% this is what release_handler needs to get a list of modules,
% since our supervisor modules list is set to 'dynamic'
% see sasl-2.1.9.2/src/release_handler_1.erl get_dynamic_mods
handle_info({From, Tag, get_modules}, State = #mochiweb_socket_server{name={local,Mod}}) ->
    From ! {element(2,Tag), [Mod]},
    {noreply, State};

% If fo-module(mochiweb_socket_server).
r some reason we can't get the module name, send empty list to avoid release_handler timeout:
handle_info({From, Tag, get_modules}, State) ->
    error_logger:info_msg("mochiweb_socket_server replying to dynamic modules request as '[]'~n",[]),
    From ! {element(2,Tag), []},
    {noreply, State};
    Mods = [Handler#handler.module || Handler <- MSL],
    ordsets:to_list(ordsets:from_list(Mods)).
	    ?reply(get_modules(MSL)),
	    loop(Parent, ServerName, MSL, Debug, false);

他確實處理了get_modules訊息,而且把模組的資訊返回去了。
但是諸位會想,為什麼要返回這個模組呢? 我們來看下如何使用mochiweb就明白了:
我們看下mochiweb/examples/keepalive/keepalive.erl

-module(keepalive).
-export([ start/1, loop/1
        ]).
%% internal export (so hibernate can reach it)
-export([ resume/3
        ]).

-define(LOOP, {?MODULE, loop}).

start(Options = [{port, _Port}]) ->
    mochiweb_http:start([{name, ?MODULE}, {loop, ?LOOP} | Options]).
...

他傳了{name, ?MODULE}進去,那麼最終這個模組keepalive會被 mochiweb_socket_server.erl的get_modules獲取的到。 也就是說升級的時候,升級模組要檢查keepalive模組是否被變更了。 這就是整個dynamic設計的目的。

流程比較繞,做好一個好的框架確實不容易,要考慮的東西太多。

這裡要注意的是misultin就沒有處理get_modules訊息,所以就會在升級的時候碰到麻煩,不成熟呀!

祝大家玩得開心!

Post Footer automatically generated by wp-posturl plugin for wordpress.

相關推薦

系統技術業餘研究 » Erlang supervisor規格dynamic行為分析

今天benjamin同學在網上問了以下問題: 我在看mochiweb和misultin的程式碼時有一些不理解的地方,以下是程式碼: init({MainSupRef, Port, OptionsTcp, AcceptorsPoolsize, RecvTimeout, SocketMode, Cus

系統技術業餘研究 » ”Erlang supervisor 極其白痴的 Bug“的澄清

2008-05-26的時候, 著名的Trustno1發表了這篇文章 http://www.iteye.com/topic/197097 抱怨Erlang supervisor 極其白痴的一個bug. 今天 @淘李福 同學重新提起這個事情: 翻到一個老帖子: http://www.iteye.com

系統技術業餘研究 » Erlang節點互聯失敗原因分析以及解決方案

今天和項仲在部署新系統的時候發現節點間ping不成功的情況,類似 1> net_adm:ping(‘[email protected]’). pang 由於這個問題比較普遍,我就記錄下一步步的排除步驟. 首先從原理上分析下!由於erlang節點間通訊是透過tcp來進行的,所以我們

系統技術業餘研究 » Erlang 17.5引入+hpds命令列控制程序預設字典大小

Erlang 17.5釋出引入控制程序預設字典大小的命令列引數: Erlang/OTP 17.5 has been released Written by Henrik, 01 Apr 2015 Some highlights of the release are: ERTS: Added co

系統技術業餘研究 » Erlang R16B03釋出,R17已發力

Erlang R16B03釋出了,通常03版本是bug fix版本,進入生產版本,官方的說明如下: OTP R16B03 is a service release with mostly a number of small corrections and user contributions. B

系統技術業餘研究 » Erlang R13B04 Installation

R13B04後erlang的原始碼編譯為了考慮移植性,就改變了編譯方式,以下是官方wiki上的安裝文件: 1. Cloning Here are the basic steps to build Erlang/OTP in the Git repository. Start by cloning:

系統技術業餘研究 » Erlang R15的記憶體delayed dealloc特性對訊息密集型程式的影響

在新的NUMA體系結構下,每個CPU都有自己的本地記憶體,如果要訪問其他CPU的記憶體,那算remote了,要走CPU之間的QPI通道,通常這樣速度會有40%的下降。 那麼對於多執行緒的程式來講,這個硬體的變化對軟體也有很大的影響。在多執行緒程式裡面,通常一個執行緒會為一個物件分配記憶體,然後把這

系統技術業餘研究 » Erlang R17新特性淺評

Erlang R17RC2 原始碼已經就緒, 參見 這裡 後續版本的釋出時間,官方的時間安排參見 這裡,摘抄如下: Preliminary dates for the upcoming release: Release: erts, emu,comp |Code stop

系統技術業餘研究 » Erlang R16支援帶顏色的控制檯

Erlang通過fix tty驅動的過濾,在R16版本支援帶顏色的控制檯,這個特性在我們做各種監控工具高亮非常有幫助,參見R16的Readme: Support ANSI in console Unix platforms will no longer filter control sequenc

系統技術業餘研究 » erlang coredump問題

早上成立濤同學問道: : :)我們最近發生了幾次宕機。。節點無緣無故就沒有了。也沒有crash dump,也不知道任何線索。 我們知道erlang的VM在正常運作的時候,如果發現erlang程式的異常或者虛擬機器資源不夠如記憶體不夠的時候,會產生erl_crash.dump檔案,裡面把crash的

系統技術業餘研究 » Erlang open_port極度影響效能的因素

Erlang的port相當於系統的IO,打開了Erlang世界通往外界的通道,可以很方便的執行外部程式。 但是open_port的效能對整個系統來講非常的重要,我就帶領大家看看open_port影響效能的因素。 首先看下open_port的文件: {spawn, Command} Star

系統技術業餘研究 » Erlang節點重啟導致的incarnation問題

今天晚上mingchaoyan同學在線上問以下這個問題: 152489 =ERROR REPORT==== 2013-06-28 19:57:53 === 152490 Discarding message {send,<<19 bytes>>} from <0.8

系統技術業餘研究 » Erlang port巧用環境變數

Erlang與外面世界的互動主要通過port來進行的,特別是和外部程式的協作,通常是通過管道進行的。 基本上有2種方法可以呼叫外部程式: 1. os:cmd 2. erlang:open_port, 這二種方式各有利弊,先看文件: os:cmd的文件參見這裡 cmd(Command) ->

系統技術業餘研究 » Erlang新新增選項 +zerts_de_busy_limit 控制節點間通訊的資料量

erlang節點間通訊預設是通過tcp通道進行的, 而且每對節點間只有一個tcp連結,所有的rpc和內建的類似monitor這樣的訊息也都是通過這個通道進行的. 當資料量過大的時候, 系統就會發出 busy distribution port警告, 同時限制資料的吞吐. 這個值預設是128k. 現

系統技術業餘研究 » Erlang程式碼反編譯以及檢視彙編碼

Erlang的程式碼是先翻譯成abstract_code,再到目的碼的,如果有符號資訊很容易恢復原始碼,通常我們部署系統的時候需要把符號資訊去掉,reltool就可以幹這個事情! 我們演示下: $ cat server.erl -module(server). -compile(export

系統技術業餘研究 » Erlang Shell實用小技巧

Erlang Shell下有很多內建的命令,在平時互動的時候很好用,文件裡面都是一行帶過,大家可能沒什麼感覺。 我來重點講解和演示下: $ erl Erlang R14B04 (erts-5.8.5) [/source] [smp:2:2] [rq:2] [async-threads:0] [h

系統技術業餘研究 » Erlang叢集RPC通道擁塞問題及解決方案

Erlang的叢集預設情況下是全聯通的,也就是當一個節點加入叢集的時候,介紹人會推薦叢集裡面所有的節點主動來和新加入的節點建立聯絡, 效果如下圖: 我們這次不講如何避免全聯通而是來講這個節點間通道的問題。 我們知道erlang的訊息傳送是透明的,只要呼叫Pid!Msg, 虛擬機器和叢集的基礎設

系統技術業餘研究 » Erlang match_spec引擎介紹和應用

match_spec是什麼呢? A “match specification” (match_spec) is an Erlang term describing a small “program” that will try to match something (either the para

系統技術業餘研究 » Erlang虛擬機器基礎設施dtrace探測點介紹和使用

最新的Erlang虛擬機器(R15B01)很大的一個改進就是加入了對dtrace探測點的支援了, 具體參見這裡, 主要目標是方便在生產實踐中定位複雜的效能問題。 目前Erlang的虛擬機器的探測點支援Linux的systemtap和freebsd的dtrace,我們剛好能夠享受的到。 作者Scot

系統技術業餘研究 » erlang的abstract code

erlang的abstract code是編譯的中間程式碼,很多工具如 erl_pp lint什麼的都是根據這個做調整的。還有進一步的parse_transform也是基於它的。 所以,瞭解它非常重要。 erts user guide裡面詳細了描述了它的定義。我這裡展示的是如何獲取到某個模組的ab