Como se mencionó en el artículo anterior, después de ejecutar ok = esockd: start (), la aplicación inicia tres módulos esockd_sup, esockd_rate_limiter y esockd_server respectivamente. Después de que estos tres módulos se inician, necesitamos inyectar nuestro propio servicio de socket, aquí tomamos echo_server como ejemplo, cuando se ejecuta en el shell
esockd:open(echo, 5000, Options, MFArgs).
Se llamarán los siguientes métodos del módulo esockd_sup:
esockd_sup:start_listener(Proto, Port, Opts, MFA);
Luego, el método start_listener (Proto, ListenOn, Opts, MFA) se llama internamente:
%% 启动子进程
-spec(start_child(supervisor:child_spec()) -> {ok, pid()} | {error, term()}).
start_child(ChildSpec) ->
supervisor:start_child(?MODULE, ChildSpec).
Inicie un proceso hijo en el módulo de escucha de esockd_sup. La especificación de este proceso hijo es la siguiente:
-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]}.
Después de ejecutar el método de inicio del proceso hijo, se ejecutará el método start_link (Proto, ListenOn, Opts, MFA) del módulo esockd_listener_sup, y luego el proceso de ejecución de la llamada interna se muestra en los comentarios del código fuente a continuación.
-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).
El start_link (Proto, ListenOn, Opts, MFA) de este módulo inicia el módulo de monitoreo esockd_listener_sup, el módulo de monitoreo esockd_acceptor_sup y el módulo de trabajo esockd_listener. Hasta ahora, se puede ejecutar en el shell:
observer:start().
Ejecute el último comando para obtener el diagrama de estructura del proceso de la siguiente manera:
Proceso <0.101.0>: esockd_listener_sup Iniciar proceso <0.102.0>: esockd_connection_sup
Proceso <0.101.0>: esockd_listener_sup Iniciar proceso <0.103.0>: esockd_acceptor_sup
Proceso <0.103.0>: esockd_acceptor_sup Iniciar proceso <0.105.0>: esockd_acceptor
Proceso <0.101.0>: esockd_listener_sup Iniciar proceso <0.104.0>: esockd_listener
Se ejecuta el programa en este nodo, se ha iniciado el nodo básico del servidor, esperando la conexión tcp del proceso cliente, y luego se analizará el módulo esockd_connection_sup.