esockd_sup模塊的行爲模式是 supervisor,該模式是一個監督者,該模塊被 esockd_app模塊內部的 start(_StartType, _StartArgs)方法內部調用,然後在esockd_sup內部會調用下面的方法:
-spec(start_link() -> {ok, pid()} | ignore | {error, term()}).
start_link() ->
%% io:format("esockd esockd_sup start_link ~n"),
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
supervisor:start_link({local, ?MODULE}, ?MODULE, []). 方法會回到esockd_sup 內部的init方法,該方法如下:
init([]) ->
%% io:format("esockd esockd_sup init ~n"),
Limiter = #{id => rate_limiter, start => {esockd_rate_limiter, start_link, []},
restart => permanent, shutdown => 5000, type => worker, modules [esockd_rate_limiter]},
Server = #{id => esockd_server, start => {esockd_server, start_link, []},
restart => permanent, shutdown => 5000, type => worker, modules => [esockd_server]},
{ok, {{one_for_one, 10, 100}, [Limiter, Server]}}.
下面把整個模塊的代碼貼上來,上面有詳細的解釋:
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
-module(esockd_sup).
%% 聲明該模塊是監督者行爲
-behaviour(supervisor).
%% 導出接口給外部模塊調動
-export([start_link/0, child_id/2]).
-export([start_listener/4, stop_listener/2, restart_listener/2]).
-export([listeners/0, listener/1]).
-export([child_spec/4, udp_child_spec/4, dtls_child_spec/4, start_child/1]).
%% supervisor callback
-export([init/1]).
%%------------------------------------------------------------------------------
%% API
%%------------------------------------------------------------------------------
-spec(start_link() -> {ok, pid()} | ignore | {error, term()}).
start_link() ->
%% io:format("esockd esockd_sup start_link ~n"),
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
-spec(start_listener(atom(), esockd:listen_on(), [esockd:option()], esockd:mfargs()) -> {ok, pid()} | {error, term()}).
start_listener(Proto, ListenOn, Opts, MFA) ->
%% io:format("esockd_sup start_listener ~w~n",[Proto]), echo
%% io:format("esockd_sup start_listener ~w~n",[ListenOn]),%% 5000
%% io:format("esockd_sup start_listener ~w~n",[Opts]), %% [{acceptors,10},{max_connections,1024},{tcp_options,[binary,{reuseaddr,true}]}]
%% io:format("esockd_sup start_listener ~w~n",[MFA]), %% {echo_server,start_link,[]}
start_child(child_spec(Proto, ListenOn, Opts, MFA)).
-spec(child_spec(atom(), esockd:listen_on(), [esockd:option()], esockd:mfargs()) -> supervisor:child_spec()).
child_spec(Proto, ListenOn, Opts, MFA) when is_atom(Proto) ->
%% Id 子進程ID標識符
%% start:StartFunc = {M, F, A}: 子程序啓動入口 M=esockd_listener_sup,F=start_link,A = [Proto, ListenOn, Opts, MFA]
%% Restart: 重啓方案
%% 1、permanent: 如果app終止了,整個系統都會停止工作(application:stop/1除外)
%% 2、transient: 如果app以normal的原因終止,沒有影響。任何其它終止原因都誰導致整個系統關閉
%% 3、temporary: app可以以任何原因終止。只產生報告,沒有其它任何影響
%% shutdown:終止策略
%% 1、brutal_kill: 無條件終止
%% 2、超時值(毫秒): 終止時,如果超時,則強制終止
%% 3、infinity: 如果子進程是監控樹,設置爲無限大,等待其終止爲止
%% type
%% 1、worker: 普通子進程
%% 2、supervisor: 子進程是監控樹
%% Modules:
%% dynamic: 當子進程是gen_event
%% [Module]: 當子進程是監控樹、gen_server或者gen_fsm,表示回調模塊名稱
#{id => child_id(Proto, ListenOn), start => {esockd_listener_sup, start_link, [Proto, ListenOn, Opts, MFA]},
restart => transient, shutdown => infinity, type => supervisor, modules => [esockd_listener_sup]}.
-spec(udp_child_spec(atom(), esockd:listen_on(), [esockd:option()], esockd:mfargs()) -> supervisor:child_spec()).
udp_child_spec(Proto, Port, Opts, MFA) when is_atom(Proto) ->
#{id => child_id(Proto, Port), start => {esockd_udp, server, [Proto, Port, Opts, MFA]},
restart => transient, shutdown => 5000, type => worker, modules => [esockd_udp]}.
-spec(dtls_child_spec(atom(), esockd:listen_on(), [esockd:option()], esockd:mfargs()) -> supervisor:child_spec()).
dtls_child_spec(Proto, Port, Opts, MFA) when is_atom(Proto) ->
#{id => child_id(Proto, Port), start => {esockd_dtls_listener_sup, start_link, [Proto, Port, Opts, MFA]},
restart => transient, shutdown => infinity, type => supervisor, modules => [esockd_dtls_listener_sup]}.
%% 啓動子進程
-spec(start_child(supervisor:child_spec()) -> {ok, pid()} | {error, term()}).
start_child(ChildSpec) ->
supervisor:start_child(?MODULE, ChildSpec).
%% 停止指定的服務監聽
-spec(stop_listener(atom(), esockd:listen_on()) -> ok | {error, term()}).
stop_listener(Proto, ListenOn) ->
%% 獲取匹配的監聽者
case match_listeners(Proto, ListenOn) of
%% 如果返回是空的,就說明沒有找到
[] -> {error, not_found};
%% 返回監聽者數組
Listeners ->
return_ok_or_error([terminate_and_delete(ChildId) || ChildId <- Listeners])
end.
%% 終止子進程
terminate_and_delete(ChildId) ->
case supervisor:terminate_child(?MODULE, ChildId) of
%% 刪除掉子進程
ok -> supervisor:delete_child(?MODULE, ChildId);
Error -> Error
end.
%% 獲取listener_sup模塊下的所有子進程,然後返回一個數組
-spec(listeners() -> [{term(), pid()}]).
listeners() ->
[{Id, Pid} || {{listener_sup, Id}, Pid, _Type, _} <- supervisor:which_children(?MODULE)].
%% 獲取一個指定的子進程
-spec(listener({atom(), esockd:listen_on()}) -> undefined | pid()).
listener({Proto, ListenOn}) ->
ChildId = child_id(Proto, ListenOn),
%% 從該進程中尋找子進程,必須 Id =:= ChildId
case [Pid || {Id, Pid, _Type, _} <- supervisor:which_children(?MODULE), Id =:= ChildId] of
%% 如果沒有找到
[] -> undefined;
%% 如果返回一個數組,從數組裏去第一個元素
L -> hd(L)
end.
%% 重新啓動監聽者
-spec(restart_listener(atom(), esockd:listen_on()) -> ok | {error, term()}).
restart_listener(Proto, ListenOn) ->
%% 獲取匹配上的監聽者
case match_listeners(Proto, ListenOn) of
%% 如果沒找到
[] -> {error, not_found};
%% 如果找到了
Listeners ->
%% 從Listeners內部去一個ChildId,然後執行terminate_and_restart(ChildId) 函數
return_ok_or_error([terminate_and_restart(ChildId) || ChildId <- Listeners])
end.
%% 終止並重啓子進程
terminate_and_restart(ChildId) ->
%% 終止掉子進程
case supervisor:terminate_child(?MODULE, ChildId) of
%% 如果終止掉了,然後就重啓
ok -> supervisor:restart_child(?MODULE, ChildId);
%% 終止錯誤,就返回一個Error
Error -> Error
end.
%% 獲取匹配的監聽者,返回一個數組
match_listeners(Proto, ListenOn) ->
%% 從esockd_sup模塊中獲取該模塊的子進程,然後執行match_listener 函數去匹配,如果返回true就加入到數組頭部
[ChildId || {ChildId, _Pid, _Type, _} <- supervisor:which_children(?MODULE), match_listener(Proto, ListenOn, ChildId)].
match_listener(Proto, ListenOn, {listener_sup, {Proto, ListenOn}}) ->
true;
match_listener(Proto, Port, {listener_sup, {Proto, {_IP, Port}}}) ->
true;
match_listener(_Proto, _ListenOn, _ChildId) ->
false.
child_id(Proto, ListenOn) -> {listener_sup, {Proto, ListenOn}}.
return_ok_or_error([]) -> ok;
return_ok_or_error([ok|Results]) ->
return_ok_or_error(Results);
return_ok_or_error([{ok, _Pid}|Results]) ->
return_ok_or_error(Results);
return_ok_or_error([{error, Reason}|_]) ->
{error, Reason}.
%%------------------------------------------------------------------------------
%% Supervisor callbacks
%%------------------------------------------------------------------------------
init([]) ->
%% io:format("esockd esockd_sup init ~n"),
Limiter = #{id => rate_limiter, start => {esockd_rate_limiter, start_link, []}, restart => permanent, shutdown => 5000, type => worker, modules => [esockd_rate_limiter]},
Server = #{id => esockd_server, start => {esockd_server, start_link, []}, restart => permanent, shutdown => 5000, type => worker, modules => [esockd_server]},
{ok, {{one_for_one, 10, 100}, [Limiter, Server]}}.
在該模塊的init方法回調過程中,會啓動esockd_rate_limiter和esockd_server 兩個模塊,接下來,我們來分析esockd_rate_limiter模塊。