EMQX源码分析---esockd_listener_sup模块源码分析

在前面的文章提到执行ok = esockd:start()后,应用分别启动三个模块esockd_sup,esockd_rate_limiter,esockd_server,这三个模块启动后,我们需要注入我们自己实现的socket服务,这里以echo_server为例,当在shell里执行

esockd:open(echo, 5000, Options, MFArgs).

的时候会调用esockd_sup模块的下面方法:

esockd_sup:start_listener(Proto, Port, Opts, MFA);

然后start_listener(Proto, ListenOn, Opts, MFA) 方法内部调用:

%% 启动子进程
-spec(start_child(supervisor:child_spec()) -> {ok, pid()} | {error, term()}).
start_child(ChildSpec) ->
	supervisor:start_child(?MODULE, ChildSpec).

在esockd_sup这个监听者模块下面启动一个子进程,这个子进程规范如下:

-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]}.

执行完启动子进程的方法后就会执行esockd_listener_sup模块的 start_link(Proto, ListenOn, Opts, MFA)方法,然后内部调用的执行过程看下面的源码注释。

-module(esockd_listener_sup).

-behaviour(supervisor).

-include("esockd.hrl").

-export([start_link/4, listener/1, acceptor_sup/1, connection_sup/1]).

%% export for dtls_listener_sup
-export([rate_limit_fun/2]).

%% supervisor callback
-export([init/1]).

%%------------------------------------------------------------------------------
%% API
%%------------------------------------------------------------------------------

%% @doc Start listener supervisor
-spec(start_link(atom(), esockd:listen_on(), [esockd:option()], esockd:mfargs()) -> {ok, pid()} | {error, term()}).
start_link(Proto, ListenOn, Opts, MFA) ->
%%    io:format("esockd_listener_sup start() ~n"),
    {ok, Sup} = supervisor:start_link(?MODULE, []),
    %% Start connection sup
    ConnSupSpec = #{id => connection_sup, start => {esockd_connection_sup, start_link, [Opts, MFA]}, restart => transient, shutdown => infinity, type => supervisor, modules => [esockd_connection_sup]},

%%    io:format("esockd_listener_sup start() ConnSupSpec ~w~n",[ConnSupSpec]),
%%    esockd_listener_sup 监控树下 启动esockd_connection_sup 连接监听
    {ok, ConnSup} = supervisor:start_child(Sup, ConnSupSpec),
%%   io:format("esockd_listener_sup supervisor:start_child(?,?) ~w~n",[ConnSupSpec]),
%%  #{id => connection_sup,modules => [esockd_connection_sup],restart => transient,shutdown => infinity,
%%  start => {esockd_connection_sup,start_link,[[{acceptors,10},{max_connections,1024},{tcp_options,[binary,{reuseaddr,true}]}],
%% {echo_server,start_link,[]}]},type => supervisor}

%% Start acceptor sup
    TuneFun = buffer_tune_fun(Opts),
    UpgradeFuns = upgrade_funs(Opts),
    StatsFun = esockd_server:stats_fun({Proto, ListenOn}, accepted),
    LimitFun = rate_limit_fun({listener, Proto, ListenOn}, Opts),
%%    io:format("esockd_listener_sup start_link TuneFun ~w~n",[TuneFun]),
%%    io:format("esockd_listener_sup start_link UpgradeFuns ~w~n",[UpgradeFuns]),
%%    io:format("esockd_listener_sup start_link LimitFun ~w~n",[LimitFun]),

    %% esockd_listener_sup 监控树下 启动esockd_acceptor_sup 接收连接请求监听
    AcceptorSupSpec = #{id => acceptor_sup, start => {esockd_acceptor_sup, start_link, [ConnSup, TuneFun, UpgradeFuns, StatsFun, LimitFun]},
                        restart => transient, shutdown => infinity, type => supervisor, modules => [esockd_acceptor_sup]},

    {ok, AcceptorSup} = supervisor:start_child(Sup, AcceptorSupSpec),
    %% Start listener
    ListenerSpec = #{id => listener, start => {esockd_listener, start_link, [Proto, ListenOn, Opts, AcceptorSup]}, restart => transient,
      shutdown => 16#ffffffff, type => worker, modules => [esockd_listener]},

%%  esockd_listener_sup 监控树下 启动esockd_listener
    {ok, _Listener} = supervisor:start_child(Sup, ListenerSpec),
    {ok, Sup}.

%% @doc Get listener.
-spec(listener(pid()) -> pid()).
listener(Sup) -> child_pid(Sup, listener).

%% @doc Get connection supervisor.
-spec(connection_sup(pid()) -> pid()).
connection_sup(Sup) -> child_pid(Sup, connection_sup).

%% @doc Get acceptor supervisor.
-spec(acceptor_sup(pid()) -> pid()).
acceptor_sup(Sup) -> child_pid(Sup, acceptor_sup).

%% @doc Get child pid with id.
child_pid(Sup, ChildId) ->
    hd([Pid || {Id, Pid, _, _}
               <- supervisor:which_children(Sup), Id =:= ChildId]).

%%------------------------------------------------------------------------------
%% Supervisor callbacks
%%------------------------------------------------------------------------------

init([]) ->
%%     io:format("esockd_listener_sup init() ~n"),
    {ok, {{rest_for_one, 10, 3600}, []}}.

%%------------------------------------------------------------------------------
%% Sock tune/upgrade functions
%%------------------------------------------------------------------------------

buffer_tune_fun(Opts) ->
    buffer_tune_fun(proplists:get_value(buffer, Opts),
                    proplists:get_bool(tune_buffer, Opts)).

%% when 'buffer' is undefined, and 'tune_buffer' is enabled...
buffer_tune_fun(undefined, true) ->
    fun(Sock) ->
        case inet:getopts(Sock, [sndbuf, recbuf, buffer]) of
            {ok, BufSizes} ->
                BufSz = lists:max([Sz || {_Opt, Sz} <- BufSizes]),
                inet:setopts(Sock, [{buffer, BufSz}]),
                {ok, Sock};
            Error -> Error
        end
    end;
buffer_tune_fun(_, _) ->
    fun(Sock) -> {ok, Sock} end.

upgrade_funs(Opts) ->
    lists:append([ssl_upgrade_fun(Opts), proxy_upgrade_fun(Opts)]).

ssl_upgrade_fun(Opts) ->
    case proplists:get_value(ssl_options, Opts) of
        undefined -> [];
        SslOpts   -> [esockd_transport:ssl_upgrade_fun(SslOpts)]
    end.

proxy_upgrade_fun(Opts) ->
    case proplists:get_bool(proxy_protocol, Opts) of
        false -> [];
        true  -> [esockd_transport:proxy_upgrade_fun(Opts)]
    end.

rate_limit_fun(Bucket, Opts) ->
    case proplists:get_value(max_conn_rate, Opts) of
        undefined ->
            fun(_) -> {1, 0} end;
        I when is_integer(I) ->
            rate_limit_fun(Bucket, I, 1);
        {Limit, Period} ->
            rate_limit_fun(Bucket, Limit, Period)
    end.

rate_limit_fun(Bucket, Limit, Period) ->
%%    io:format("esockd_listener_sup rate_limit_fun() Bucket ~w~n",[Bucket]),
%%    io:format("esockd_listener_sup rate_limit_fun() Limit ~w~n",[Limit]),
%%    io:format("esockd_listener_sup rate_limit_fun() Period ~w~n",[Period]),
    ok = esockd_rate_limiter:create(Bucket, Limit, Period),
    fun(I) -> esockd_rate_limiter:consume(Bucket, I) end.



%% ok = esockd:start().
%% Options = [{acceptors, 1}, {max_connections, 1024}, {tcp_options, [binary, {reuseaddr, true}]}].
%% MFArgs = {echo_server, start_link, []},esockd:open(echo, 5000, Options1, MFArgs).

该模块的start_link(Proto, ListenOn, Opts, MFA)内部分别启动了esockd_listener_sup 监控模块,esockd_acceptor_sup 监控模块和 esockd_listener工作者模块。到现在为止可以通过在shell里面执行:

observer:start().

执行上个命令可以到进程结构图如下:

EMQX源码分析---esockd_listener_sup模块源码分析_第1张图片

进程<0.101.0>:esockd_listener_sup启动进程<0.102.0>:esockd_connection_sup

进程<0.101.0>:esockd_listener_sup 启动进程<0.103.0>:esockd_acceptor_sup

进程<0.103.0>:esockd_acceptor_sup 启动进程<0.105.0>:esockd_acceptor

进程<0.101.0>:esockd_listener_sup 启动进程<0.104.0>:esockd_listener

程序执行到这个节点,服务器的基本结点已经启动,等待客户进程tcp连接,接下来将对esockd_connection_sup模块进行分析。

你可能感兴趣的:(Erlang)