EMQX源碼分析---esockd_sup 模塊源碼分析

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模塊。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章