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