範例內容包含了建立伺服器、監督伺服器、登錄錯誤到日誌、偵測警報這些功能,最後再將整個程式碼包裝成一個獨立的OTP應用。
建立兩個 otp server,一個用來產生質數,一個計算面積,系統錯誤會造成當機,需要一個偵測機制來重新啟動 server,也就是 supervision tree,這可以產生一個監督者,負責觀察 server,當server crash就重新啟動。
OTP 錯誤登錄器可紀錄所有錯誤,產生錯誤報告,另外因為計算很大的質數可能會造成 CPU 過熱,為了避免這個問題,使用 OTP 事件處理框架,來產生警報,以便配置強力風扇。
- 說明通用事件處理器的機制
- 說明錯誤登錄器的機制
- 加入警報管理
- 寫出兩個應用伺服器
- 實作監督樹,並將伺服器加入到監督樹中
- 把整個程式包裝成一個 OTP 應用
通用事件處理器
當程式中,有值得注意的事情 event 發生了,就送出事件訊息到註冊行程,只需要送出訊息通知有事情發生了,不在意送出訊息後會發生什麼事情。
RegProcName ! {event, E}
第一個通用事件處理器
-module(event_handler).
-export([make/1, add_handler/2, event/2]).
%% make a new event handler called Name
%% the handler function is no_op -- so we do nothing with the event
%% 產生一個 event handler,但收到 event 後,不做任何事情
make(Name) ->
register(Name, spawn(fun() -> my_handler(fun no_op/1) end)).
%% 發送 event及處理該 event 的函數 Fun
add_handler(Name, Fun) -> Name ! {add, Fun}.
%% generate an event
event(Name, X) -> Name ! {event, X}.
my_handler(Fun) ->
receive
{add, Fun1} ->
my_handler(Fun1);
{event, Any} ->
% 一開始是使用 no_op(_),後來可透過 add_handler 動態修改事件處理函數
(catch Fun(Any)),
my_handler(Fun)
end.
no_op(_) -> void.
測試
1> event_handler:make(errors).
true
2> event_handler:event(errors, hi).
{event,hi}
發送 event 及 處理 event 的函數
想讓 event handler 做些事情,必須寫一個 callback module,並安裝在事件處理器中。
-module(motor_controller).
-export([add_event_handler/0]).
add_event_handler() ->
event_handler:add_handler(errors, fun controller/1).
controller(too_hot) ->
io:format("Turn off the motor~n");
controller(X) ->
io:format("~w ignored event: ~p~n",[?MODULE, X]).
測試
3> motor_controller:add_event_handler().
{add,#Fun<motor_controller.0.125151531>}
4> event_handler:event(errors, cool).
motor_controller ignored event: cool
{event,cool}
5> event_handler:event(errors, too_hot).
Turn off the motor
{event,too_hot}
very late binding
如果寫一個函數把 event_handler:event 封裝起來
too_hot() ->
event_handler:event(errors, too_hot).
如果出錯就呼叫 too_hot(),這會讓事件處理的模式固定。
erlang 處理事件的方式,讓我們能解除「事件發生」和「事件處理」之間的關係,我們可以在任何時候改變處理的方式,只要發送新的事件處理函數給事件處理器,就可以動態調整事件處裡的方法。也可以利用這個機制,在不停止server的狀況下,動態升級程式。
錯誤記錄器 error logger
OTP 內建可客製化的 event logger,可從三個部份討論(1) 函數呼叫,如何登錄錯誤到日誌 (2) configuration 設定錯誤登錄資料儲存的方式 (3) 錯誤的分析報告
登錄錯誤到日誌
@spec error_logger:error_msg(String) -> ok
發送錯誤訊息到 error logger
@spec error_logger:error_msg(Format, Data) -> ok
發送錯誤訊息到 error logger,參數跟 io:format(Format, Data) 一樣
@spec error_logger:error_report(Report) -> ok
發送錯誤報告到 error logger
@type Report = [{Tag, Data} | term() | string() | term()]
@type Tag = term()
@type Data = term()
configuration
預設可以在 erlang shell 中看到所有錯誤,我們可以另外將錯誤寫到單一文字檔,或是製作一天一個檔案的日誌檔,也可以製作轉動的環狀日誌檔。
標準的錯誤登錄器
啟動 erl 時,加入 boot 參數,預設值就是 start_clean
這是「適合編程」的環境,只有一種簡單的錯誤日誌形式
> erl -boot start_clean
這是「適合執行產品系統」的環境,SASL: System Architecture Support Libraries 會負責錯誤登錄、負載保護等等
> erl -boot start_sasl
日誌檔案的組態設定,最好放在 configuration file 裡面。
如果啟用 SASL,但又沒有提供 config file,就會發生錯誤。
D:\projectcase\erlang\erlangotp\ebin>erl -boot start_sasl
=PROGRESS REPORT==== 20-Feb-2014::11:40:27 ===
supervisor: {local,sasl_safe_sup}
started: [{pid,<0.34.0>},
{name,alarm_handler},
{mfargs,{alarm_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]
.....
=PROGRESS REPORT==== 20-Feb-2014::11:40:27 ===
application: sasl
started_at: nonode@nohost
Eshell V5.10.4 (abort with ^G)
1>
控制日誌的內容
錯誤登錄會自動產生三種報告
- 監督者報告
當 OTP 監督者開始或停止監督一個 process 時 所發出的
- 進度報告
當 OTP 監督者啟動或停止時 所發出的
- 當機報告
當一個 OTP 行為終止的理由不正常 或 關閉時 所發出的
另外可以主動呼叫 error_handler module 的 function,來產生幾種等級的日誌報告,error_type 的類型有 error | error_report | info_msg | info_report | warning_msg | warning_report | crash_report | supervisor_report | progress,在分析日誌時,就能使用標籤來幫助我們決定那個日誌項目需要被檢視。
範例1
這會得到錯誤報告,而不是進度或其他報告
%% elog1.config
%% no tty
[{sasl, [
{sasl_error_logger, false}
]}].
測試
>erl -boot start_sasl -config elog1
Eshell V5.10.4 (abort with ^G)
1> error_logger:error_msg("I'm Error\n").
=ERROR REPORT==== 20-Feb-2014::11:54:08 ===
I'm Error
ok
範例2
會把 shell 所有東西複製到檔案中
%% elog2.config
%% single text file - minimal tty
[{sasl, [
%% All reports go to this file
{sasl_error_logger, {file, "d:/temp/error_logs/logfile"}}
]}].
測試
>erl -boot start_sasl -config elog2
Eshell V5.10.4 (abort with ^G)
1> error_logger:error_msg("I'm Error\n").
=ERROR REPORT==== 20-Feb-2014::12:01:01 ===
I'm Error
ok
如果 d:/temp/error_logs 目錄不存在,就不會產生檔案 logfile,檔案的內容如下:
=PROGRESS REPORT==== 20-Feb-2014::12:00:59 ===
supervisor: {local,sasl_safe_sup}
started: [{pid,<0.35.0>},
{name,alarm_handler},
{mfargs,{alarm_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]
....
範例3
所有錯誤日誌都會進入 rotating log 裡面
%% rotating log and minimal tty
[{sasl, [
{sasl_error_logger, false},
%% define the parameters of the rotating log
%% the log file directory 這裡是填目錄
{error_logger_mf_dir,"d:/temp/error_logs"},
%% # bytes per logfile
{error_logger_mf_maxbytes,10485760}, % 10 MB
%% maximum number of logfiles
{error_logger_mf_maxfiles, 10}
]}].
> erl -boot start_sasl -config elog3
範例4
在正式環境,增加 {errlog_type, error} 只記錄 error
%% rotating log and errors
[{sasl, [
%% minimise shell error logging
{sasl_error_logger, false},
%% only report errors
{errlog_type, error},
%% define the parameters of the rotating log
%% the log file directory
{error_logger_mf_dir,"d:/temp/error_logs"},
%% # bytes per logfile
{error_logger_mf_maxbytes,10485760}, % 10 MB
%% maximum number of
{error_logger_mf_maxfiles, 10}
]}].
分析錯誤
rb 模組負責分析錯誤。
rb:help(). 可取得 help
rb:start([{max, 20}]). 啟動報告瀏覽器,只讀取 20 筆記錄
rb:show(1). 顯示 1 號錯誤
rb:list(). 錯誤列表
rb:grep(RegExp). 找出所有符合 RegExp 正規表示式 的報告
>erl -boot start_sasl -config elog3
1> rb:start([{max, 20}]).
rb: reading report...done.
{ok,<0.42.0>}
2> rb:show(1).
PROGRESS REPORT <0.7.0> 2014-02-20 15:57:19
===============================================================================
application sasl
started_at nonode@nohost
ok
警報管理
警報處理器是 OTP gen_event 行為的 callback module。
-module(my_alarm_handler).
-behaviour(gen_event).
%% gen_event callbacks
-export([init/1, handle_event/2, handle_call/2,
handle_info/2, terminate/2]).
%% init(Args) must return {ok, State}
init(Args) ->
io:format("*** my_alarm_handler init:~p~n",[Args]),
{ok, 0}.
handle_event({set_alarm, tooHot}, N) ->
error_logger:error_msg("*** Tell the Engineer to turn on the fan~n"),
{ok, N+1};
handle_event({clear_alarm, tooHot}, N) ->
error_logger:error_msg("*** Danger over. Turn off the fan~n"),
{ok, N};
handle_event(Event, N) ->
io:format("*** unmatched event:~p~n",[Event]),
{ok, N}.
handle_call(_Request, N) -> Reply = N, {ok, Reply, N}.
handle_info(_Info, N) -> {ok, N}.
terminate(_Reason, _N) -> ok.
測試
>erl -boot start_sasl -config elog3
% 一開始的 set_alarm,什麼事都不會發生,因為預設的處理器不做任何事情
1> alarm_handler:set_alarm(tooHot).
ok
=INFO REPORT==== 20-Feb-2014::15:01:57 ===
alarm_handler: {set,tooHot}
% 替換 alarm_handler
2> gen_event:swap_handler(alarm_handler, {alarm_handler, swap}, {my_alarm_handler, xyz}).
*** my_alarm_handler init:{xyz,{alarm_handler,[tooHot]}}
ok
% 再一次呼叫 set_alarm 就會改用自訂的 alarm handler
3> alarm_handler:set_alarm(tooHot).
=ERROR REPORT==== 20-Feb-2014::15:02:49 ===
*** Tell the Engineer to turn on the fan
ok
% 清除 tooHot 的警報
4> alarm_handler:clear_alarm(tooHot).
=ERROR REPORT==== 20-Feb-2014::15:03:09 ===
*** Danger over. Turn off the fan
ok
查看警報的日誌報告
>erl -boot start_sasl -config elog3
Eshell V5.10.4 (abort with ^G)
1> rb:start([{max,20}]).
rb: reading report...done.
{ok,<0.42.0>}
2> rb:list().
No Type Process Date Time
== ==== ======= ==== ====
14 progress <0.30.0> 2014-02-20 15:57:19
13 progress <0.30.0> 2014-02-20 15:57:19
12 progress <0.30.0> 2014-02-20 15:57:19
11 progress <0.30.0> 2014-02-20 15:57:19
10 progress <0.24.0> 2014-02-20 15:57:19
9 progress <0.30.0> 2014-02-20 15:58:12
8 info_report <0.30.0> 2014-02-20 16:00:25
7 error <0.30.0> 2014-02-20 16:00:36
6 error <0.30.0> 2014-02-20 16:00:39
5 progress <0.30.0> 2014-02-20 16:02:03
4 progress <0.30.0> 2014-02-20 16:02:03
3 progress <0.30.0> 2014-02-20 16:02:03
2 progress <0.30.0> 2014-02-20 16:02:03
1 progress <0.24.0> 2014-02-20 16:02:03
ok
3> rb:show(6).
ERROR REPORT <0.34.0> 2014-02-20 16:00:39
===============================================================================
*** Danger over. Turn off the fan
ok
4> rb:show(7).
ERROR REPORT <0.34.0> 2014-02-20 16:00:36
===============================================================================
*** Tell the Engineer to turn on the fan
ok
4> rb:stop().
ok
應用伺服器
有兩個伺服器
質數
-module(prime_server).
-behaviour(gen_server).
-export([new_prime/1, start_link/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
%% client functions
start_link() ->
%% 呼叫 gen_server:start_link(Name, CallBackMod, StartArgs, Opts) 啟動 server
%% 第一個呼叫的是 Mod:init(StartArgs)
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
new_prime(N) ->
%% 20000 is a timeout (ms)
gen_server:call(?MODULE, {prime, N}, 20000).
%% 6 server callback functions
%% 啟動時會被呼叫的函數,回傳值 {ok, State} 裡面的 State 會在其他函數中使用
init([]) ->
%% Note we must set trap_exit = true if we
%% want terminate/2 to be called when the application is stopped
%% 一定要設定 trap_exit 為 true,這樣在 app 停止時,才會呼叫 terminate/2
process_flag(trap_exit, true),
io:format("~p starting~n",[?MODULE]),
{ok, 0}.
handle_call({prime, K}, _From, N) ->
{reply, make_new_prime(K), N+1}.
handle_cast(_Msg, N) -> {noreply, N}.
handle_info(_Info, N) -> {noreply, N}.
terminate(_Reason, _N) ->
io:format("~p stopping~n",[?MODULE]),
ok.
code_change(_OldVsn, N, _Extra) -> {ok, N}.
% private function
make_new_prime(K) ->
if
K > 100 ->
alarm_handler:set_alarm(tooHot),
N = lib_primes:make_prime(K),
alarm_handler:clear_alarm(tooHot),
N;
true ->
lib_primes:make_prime(K)
end.
面積
-module(area_server).
-behaviour(gen_server).
-export([area/1, start_link/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
%% client functions
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
area(Thing) ->
gen_server:call(?MODULE, {area, Thing}).
%% 6 server callback functions
%% 啟動時會被呼叫的函數,回傳值 {ok, State} 裡面的 State 會在其他函數中使用
init([]) ->
%% Note we must set trap_exit = true if we
%% want terminate/2 to be called when the application
%% is stopped
%% 一定要設定 trap_exit 為 true,這樣在 app 停止時,才會呼叫 terminate/2
process_flag(trap_exit, true),
io:format("~p starting~n",[?MODULE]),
{ok, 0}.
handle_call({area, Thing}, _From, N) -> {reply, compute_area(Thing), N+1}.
handle_cast(_Msg, N) -> {noreply, N}.
handle_info(_Info, N) -> {noreply, N}.
terminate(_Reason, _N) ->
io:format("~p stopping~n",[?MODULE]),
ok.
code_change(_OldVsn, N, _Extra) -> {ok, N}.
%% private function
compute_area({square, X}) -> X*X;
compute_area({rectongle, X, Y}) -> X*Y.
監督樹
所有監督樹都是行程樹,樹上方的行程(監督者)會監控下面的行程(工作者),如果工作者發生問題,監督者就可以重新啟動它。監督樹有兩種:
- 一對一監督樹
如果工作者失敗,就會被監督者重新啟動。監督者會同時監督多個行程,一對一就是針對每一個行程,都會單獨地進行重新啟動的機制。
- 一對多監督樹
如果任一工作者失敗,監督者監控的所有工作者都會被 kill 掉,然後重新啟動所有工作行程。
監督樹的指定是透過下面這樣的函數
init(...) ->
{ok, {RestartStrategy, MaxRestarts, Time},
[Worker1, Worker2, ...]}.
RestartStrategy 有兩種 (1) one_for_one (2) all_for_one
MaxRestarts 與 Time 是設定一個 「重新啟動的頻率」,如果監督者在 Time 秒內重新啟動工作者超過 MaxRestarts 次,此監督者就會終止所有工作行程,並終止自己。這個機制是為了避免發生「行程當機->重新啟動->行程當機」 的無窮迴圈。
Worker1, Worker2,... 都是 tuple,描述如何重新啟動每一個工作者行程。
Worker
{Tag, {Mod, Fun, ArgList},
Restart,
Shutdown,
Type,
[Mod1]}
Tag
atom,用來稱呼該工作者行程
{Mod, Fun, ArgList}
定義了監督者會用到的函數,當作 apply(Mod, Fun, ArgList) 的參數
Restart = permanent | transient | temporary
permanent 行程一定會被重新啟動
transient 只有在「因非正常結束碼而停止」之後,才會重新啟動
temporary 不會被重新啟動
Shutdown
停止 server 時所能耗用的最大時間,超過時間,就會被直接 kill
Type = worker | supervisor
被監督行程的型別,我們可以在工作者行程的位置改放監督者行程,建構出監督者的樹
[Mod1]
如果子行程是監督者或 gen_server 行為callback module,這就是 callback module 的名稱
範例
-module(sellaprime_supervisor).
-behaviour(supervisor).
-export([start/0, start_in_shell_for_testing/0, start_link/1, init/1]).
start() ->
spawn(fun() ->
supervisor:start_link({local,?MODULE}, ?MODULE, _Arg = [])
end).
start_in_shell_for_testing() ->
{ok, Pid} = supervisor:start_link({local,?MODULE}, ?MODULE, _Arg = []),
unlink(Pid).
start_link(Args) ->
supervisor:start_link({local,?MODULE}, ?MODULE, Args).
init([]) ->
%% Install my personal error handler
% 替換自訂的 error handler
gen_event:swap_handler(alarm_handler,
{alarm_handler, swap},
{my_alarm_handler, xyz}),
{ok, {{one_for_one, 3, 10},
[{tag1,
{area_server, start_link, []},
permanent,
10000,
worker,
[area_server]},
{tag2,
{prime_server, start_link, []},
permanent,
10000,
worker,
[prime_server]}
]}}.
啟動系統
>erl -boot start_sasl -config elog3
Eshell V5.10.4 (abort with ^G)
1> sellaprime_supervisor:start_in_shell_for_testing().
*** my_alarm_handler init:{xyz,{alarm_handler,[]}}
area_server starting
prime_server starting
true
2> area_server:area({square, 10}).
100
% area_server 會停掉,並重新啟動
3> area_server:area({rectangle, 10, 20}).
area_server stopping
area_server starting
** exception exit: {{function_clause,
[{area_server,compute_area,
[{rectangle,10,20}],
[{file,
"d:/projectcase/erlang/erlangotp/src/area_serv
er.erl"},
{line,50}]},
{area_server,handle_call,3,
[{file,
"d:/projectcase/erlang/erlangotp/src/area_serv
er.erl"},
{line,37}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,585}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,239}]}]},
{gen_server,call,[area_server,{area,{rectangle,10,20}}]}}
in function gen_server:call/2 (gen_server.erl, line 180)
4>
=ERROR REPORT==== 20-Feb-2014::18:08:09 ===
** Generic server area_server terminating
** Last message in was {area,{rectangle,10,20}}
** When Server state == 1
** Reason for termination ==
** {function_clause,
[{area_server,compute_area,
[{rectangle,10,20}],
[{file,"d:/projectcase/erlang/erlangotp/src/area_server.erl"},
{line,50}]},
{area_server,handle_call,3,
[{file,"d:/projectcase/erlang/erlangotp/src/area_server.erl"},
{line,37}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,585}]},
{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,239}]}]}
4> area_server:area({square, 20}).
400
5> prime_server:new_prime(20).
Generating a 20 digit prime ..........................
11511342604390163281
6> prime_server:new_prime(120).
Generating a 120 digit prime .
=ERROR REPORT==== 20-Feb-2014::18:08:48 ===
*** Tell the Engineer to turn on the fan
................................................................................
31109310729316846838871494679201035944025034010885289775272667568919068598600821
8311374378220338982779418876998717503511
=ERROR REPORT==== 20-Feb-2014::18:08:51 ===
*** Danger over. Turn off the fan
接下來查閱報表。
7> rb:start([{max, 20}]).
rb: reading report...done.
{ok,<0.53.0>}
8> rb:list().
No Type Process Date Time
== ==== ======= ==== ====
13 progress <0.30.0> 2014-02-20 18:07:21
12 progress <0.30.0> 2014-02-20 18:07:21
11 progress <0.30.0> 2014-02-20 18:07:21
10 progress <0.30.0> 2014-02-20 18:07:21
9 progress <0.24.0> 2014-02-20 18:07:21
8 progress <0.24.0> 2014-02-20 18:07:39
7 progress <0.24.0> 2014-02-20 18:07:39
6 error <0.24.0> 2014-02-20 18:08:09
5 crash_report area_server 2014-02-20 18:08:09
4 supervisor_report <0.24.0> 2014-02-20 18:08:09
3 progress <0.24.0> 2014-02-20 18:08:09
2 error <0.30.0> 2014-02-20 18:08:48
1 error <0.30.0> 2014-02-20 18:08:51
ok
9> rb:show(5).
CRASH REPORT <0.43.0> 2014-02-20 18:08:09
===============================================================================
Crashing process
initial_call {area_server,init,['Argument__1']}
pid <0.43.0>
registered_name area_server
error_info
{exit,
{function_clause,
[{area_server,compute_area,
[{rectangle,10,20}],
[{file,
"d:/projectcase/erlang/erlangotp/src/area_server.erl",
{line,50}]},
{area_server,handle_call,3,
[{file,
"d:/projectcase/erlang/erlangotp/src/area_server.erl",
{line,37}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,585}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,239}]}]},
[{gen_server,terminate,6,[{file,"gen_server.erl"},{line,744}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,239}]}]}
ancestors [sellaprime_supervisor,<0.40.0>]
messages []
links [<0.42.0>]
dictionary []
trap_exit true
status running
heap_size 987
stack_size 27
reductions 200
ok
包裝應用
%% sellaprime.app
{application, sellaprime,
[{description, "The Prime Number Shop"},
{vsn, "1.0"},
{modules, [sellaprime_app, sellaprime_supervisor, area_server,
prime_server, lib_lin, lib_primes, my_alarm_handler]},
{registered,[area_server, prime_server, sellaprime_super]},
{applications, [kernel,stdlib]},
{mod, {sellaprime_app,[]}},
{start_phases, []}
]}.
sellaprime.app 的 callback module,必須有 start/2 跟 stop/1 function
% sellaprime_app.erl
-module(sellaprime_app).
-behaviour(application).
-export([start/2, stop/1]).
start(_Type, StartArgs) ->
sellaprime_supervisor:start_link(StartArgs).
stop(_State) ->
ok.
測試
application:loaded_applications(). 取得 otp 載入的applications
application:load(sellaprime). 載入 sellaprime.app
application:start(sellaprime). 啟動 sellaprime
application:stop(sellaprime). 停止 sellaprime
application:unload(sellaprime). 卸載 sellaprime.app
>erl -boot start_sasl -config elog3
Eshell V5.10.4 (abort with ^G)
1> application:loaded_applications().
[{kernel,"ERTS CXC 138 10","2.16.4"},
{sasl,"SASL CXC 138 11","2.3.4"},
{stdlib,"ERTS CXC 138 10","1.19.4"}]
2> application:load(sellaprime).
ok
3> application:loaded_applications().
[{sellaprime,"The Prime Number Shop","1.0"},
{kernel,"ERTS CXC 138 10","2.16.4"},
{sasl,"SASL CXC 138 11","2.3.4"},
{stdlib,"ERTS CXC 138 10","1.19.4"}]
4> application:start(sellaprime).
*** my_alarm_handler init:{xyz,{alarm_handler,[]}}
area_server starting
prime_server starting
ok
5> area_server:area({square, 20}).
400
6> prime_server:new_prime(20).
Generating a 20 digit prime ...................
28361723754284313301
7> application:stop(sellaprime).
prime_server stopping
area_server stopping
ok
=INFO REPORT==== 21-Feb-2014::10:29:12 ===
application: sellaprime
exited: stopped
type: temporary
8> application:loaded_applications().
[{sellaprime,"The Prime Number Shop","1.0"},
{kernel,"ERTS CXC 138 10","2.16.4"},
{sasl,"SASL CXC 138 11","2.3.4"},
{stdlib,"ERTS CXC 138 10","1.19.4"}]
9> application:unload(sellaprime).
ok
10> application:loaded_applications().
[{kernel,"ERTS CXC 138 10","2.16.4"},
{sasl,"SASL CXC 138 11","2.3.4"},
{stdlib,"ERTS CXC 138 10","1.19.4"}]
11> init:stop().
ok
完整的應用包含的檔案
- area_server.erl
area_server 這是 gen_server 的 callback module
- prime_server.erl
prime_server 這是 gen_server 的 callback module
2.1 lib_primes.erl, lib_lin.erl
產生 primes 的 module
- sellaprime_supervisor.erl
監督者 callback module
- sellaprime_app.erl
sellaprime application callback module
- my_alarm_handler.erl
gen_event 事件處理 callback module
- sellaprime.app
sellaprime application
- elog3.config
error logger configuration
運作流程如下
啟動系統 > erl -boot start_sasl -config elog3.config
sellaprime.app 必須在 erlang 啟動的根目錄,或在該目錄的某個次目錄
應用控制器會在 sellaprime.app 中取得 {mod, ...} 宣告,也就是 sellaprime_app.erl
呼叫 sellaprime_app:start/2
sellaprime_app:start/2 內部呼叫 sellaprime_supervisor:start_link/2,然後啟動了 sellaprime 監督者
呼叫 sellaprime_supervisor:init/1,這會安裝一個 error logger 處理器,並回傳重新啟動的策略
sellaprime 的監督者會啟動 prime_server 與 area_server
呼叫 application:stop(sellaprime) 或 init:stop() 就可以停止 sellaprime application
GUI 應用監控器
這是可以檢視 application 的 GUI 程式
appmon:start().
參考
Erlang and OTP in Action
Programming Erlang: Software for a Concurrent World