在前面的文章提到执行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().
执行上个命令可以到进程结构图如下:
进程<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模块进行分析。