1>application:start(log4erl).
我們就從這一行命令開始說起吧,回車之后可以把log4erl應用程序啟動起來.Erlang/OTP中的能完成特定功能集合的組件被稱為application. ,application是Erlang代碼和功能組織的形式之一([Erlang 0015]Erlang OTP設計原則).application的設計目的是通過運行一個或者多個進程來完成一定功能.為了能夠管理這些進程的生命周期,需要通過supervisor進行管理.
application:start(log4erl).實際上是執行了application:start(log4erl,temporary).第二個參數表示應用程序的啟動類型(Start_Type).啟動類型還可以是permanent transient,三種啟動類型的區別是:如果Start_Type==permanent 應用程序終止之后所有其它的應用程序和運行時系統都會死掉;如果Start_Type==transient 應用程序終止的原因是normal,這個消息會報出來但是其它應用程序不會重啟,如果應用程序終止的原因不是normal,其他應用程序和運行時也會跟着死掉;如果Start_Type==temporary 應用程序死掉會報錯誤出來但是其它應用程序不受影響.注意實踐過程中很少使用transient參數,因為進程樹崩掉的時候,進程正常退出原因是shutdown不是normal,這個我之前的文章提到過:[Erlang 0017]Erlang/OTP基礎模塊 proc_lib 無論使用哪種類型啟動,直接調用stop方法關閉application是不會影響到其它應用程序的.
之前遇到過一個比較嚴重的問題導致應用崩掉,當時就有同事問為什么erlang的進程還在,或者說運行時還活着?其實就是因為我們啟動應用程序的時候默認使用了temporary;
application配置文件
application的配置文件(Application Resource File)基本上確定了application的格局.如果啟動失敗了就要檢查一下配置文件了,配置文件命名要求必須與application命名一致,即必須要有一個log4erl.app文件:
{application, log4erl,
[{description, "Logger for erlang in the spirit of Log4J"},
{vsn, "0.9.0"},
{modules, [console_appender, %%該配置節可以留空 []
dummy_appender,
email_msg,
error_logger_log4erl_h,
% ................省略部分模塊
xml_appender]},
{registered,[log4erl]}, %%這個應用程序將使用的注冊名
{applications, [kernel,stdlib]}, %%注意這個配置節是指定當前應用程序依賴哪些應用程序,類似Windows服務的依賴關系
{mod, {log4erl,[]}},
%{env,[{key,value},{k2,v2}]}, %env配置節,里面以key-value的形式組織配置數據.可以用application:get_env/2讀取.
{start_phases, []}
]}.
application啟動過程
Erlang運行時啟動時,application controller進程會隨着Kernel應用程序啟動,在erlang shell中可以通過whereis(application_controller)找到它.應用程序相關的操作都由它來協調完成,它通過application模塊暴露出來的接口來實現應用程序的加載,卸載,啟動,停止等等.
應用程序啟動之前首先會進行加載load,如果沒有沒有加載application controller會首先執行load/1.加載完成猴子偶application controller會檢查application配置文件中的applications配置節中所列出的應用程序都已經在運行.如果有依賴項還沒有啟動,就拋出{error,{not_started,App}}的錯誤.
完成依賴項檢查之后application controller會為application創建application master.application master會成為該application中所有進程的Group Leader.(group leader決定了輸出重定向的位置,參見這個問題:如何從A節點調用B節點的一個函數,結果輸出到B節點呢?) .通過配置文件的mod配置節,application master知道要調用回調函數log4erl:start/2.
application behavior有兩個回調函數需要實現 start/2 stop/1分別來指定應用程序如何啟動,如何停止.啟動application的時候調用start方法通過啟動頂層的supervisor來創建進程樹.log4erl實現了application的behavior,它的start方法也是中規中矩的啟動了頂層superior:
start(_Type, []) -> log4erl_sup:start_link(?DEFAULT_LOGGER). %%這里?DEFAULT_LOGGER宏的值為default_logger
start對返回值是有規格要求的:start(StartType, StartArgs) -> {ok, Pid} | {ok, Pid, State}
當返回結果不一致的時候應用程序自然啟動失敗,初學的時候最常見的錯誤就是加一下輸出看看是不是啟動了,結果就悲劇了.比如下面的代碼,start函數的返回值是最后一個表達式io:format的值,而這個結果是ok,不符合application對start回調函數結果的要求.
start(Startype,Arg) -> demo_sup:start_link(), io:format("demo app start").
application停止過程
要停止application,application master首先會調用Module:prep_stop/1(如果存在),然后告知頂層的supervisor關閉(shutdown),shutdown的過程:整個監控樹的所有進程和包含的應用程序按照啟動的逆序終止.shutdown完成之后,application master調用Module:stop/1.最后application master自己終止掉.application停止了,但是依然處於已加載的狀態(loaded).
%log4erl stop stop(_State) -> log_filter_codegen:reset(), ok.
啟動 appmon:start()獲得更為直觀的印象.下面是log4erl應用程序進程的組織方式,log4erl_sup是log4erl application的頂層supervisor,上面的<0.42.0>和<0.43.0>按照上文的介紹應該是application master進程,我們通過取進程信息來驗證:
[{current_function,{ application_master,main_loop,2}},
{initial_call,{proc_lib,init_p,5}},
{status,waiting},
{message_queue_len,0},
{messages,[]},
{links,[<0.6.0>,<0.43.0>]},
{dictionary,[{'$ancestors',[<0.41.0>]},
{'$initial_call',{application_master,init,4}}]},
{trap_exit,true},
{error_handler,error_handler},
{priority,normal},
{group_leader,<0.42.0>},
{total_heap_size,233},
{heap_size,233},
{stack_size,6},
{reductions,23},
{garbage_collection,[{min_bin_vheap_size,46368},
{min_heap_size,233},
{fullsweep_after,65535},
{minor_gcs,0}]},
{suspending,[]}]
[{current_function,{application_master,loop_it,4}},
{initial_call,{application_master,start_it,4}},
{status,waiting},
{message_queue_len,0},
{messages,[]},
{links,[<0.42.0>,<0.44.0>]},
{dictionary,[]},
{trap_exit,true},
{error_handler,error_handler},
{priority,normal},
{group_leader,<0.42.0>},
{total_heap_size,233},
{heap_size,233},
{stack_size,5},
{reductions,65},
{garbage_collection,[{min_bin_vheap_size,46368},
{min_heap_size,233},
{fullsweep_after,65535},
{minor_gcs,0}]},
{suspending,[]}]
=INFO REPORT==== 27-Dec-2011::20:29:47 ===
application: log4erl
exited: stopped
type: temporary
7> erlang:process_info(pid(0,43,0)).
undefined
8> erlang:process_info(pid(0,42,0)).
undefined
9>