Análisis de código fuente EMQX --- análisis de código fuente esockd

1. Introducción a las funciones básicas

  1. Esockd es un marco de servidor de sockets TCP / SSL asíncrono sin bloqueo para erlang

  2. Admite grupo de aceptadores y aceptación asincrónica

  3. Admite servidor UDP / DTLS

  4. Admite el número máximo de gestión de conexiones

  5. Admite la negación dinámica y permite la ip especificada

  6. Soporte de límite de velocidad del cliente

  7, soporte ip6

2. Diagrama de arquitectura

En tercer lugar, comience desde el archivo esockd.erl

Este módulo contiene API central, API de gestión, definición de función de herramienta, definición de tipo y otras funciones 

El código fuente de esockd es el siguiente:

%% 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).

-include("esockd.hrl").

-export([start/0]).

%% Core API
-export([open/4, open_udp/4, open_dtls/4, close/2, close/1]).
-export([reopen/1, reopen/2]).
-export([child_spec/4, udp_child_spec/4, dtls_child_spec/4]).

%% Management API
-export([listeners/0, listener/1]).
-export([get_stats/1, get_options/1, get_acceptors/1]).
-export([get_max_connections/1, set_max_connections/2, get_current_connections/1]).
-export([get_shutdown_count/1]).

%% Allow, Deny API
-export([get_access_rules/1, allow/2, deny/2]).

%% Utility functions
-export([parse_opt/1, ulimit/0, fixaddr/1, to_string/1]).


-type(proto() :: atom()).
%% transport()定义为module类型
-type(transport() :: module()).
%%
-type(udp_transport() :: {udp | dtls, pid(), inet:socket()}).
%% sock() 定义为 esockd_transport:sock()
-type(sock() :: esockd_transport:sock()).

-type(mfargs() :: atom() | {atom(), atom()} | {module(), atom(), [term()]}).

-type(sock_fun() :: fun((esockd_transport:sock()) -> {ok, esockd_transport:sock()} | {error, term()})).

-type(option() :: {acceptors, pos_integer()}|{max_connections, pos_integer()}
                | {max_conn_rate, pos_integer() | {pos_integer(), pos_integer()}}
                | {access_rules, [esockd_access:rule()]}|{shutdown, brutal_kill | infinity | pos_integer()}
                | tune_buffer | {tune_buffer, boolean()}| proxy_protocol | {proxy_protocol, boolean()}
                | {proxy_protocol_timeout, timeout()}| {ssl_options, [ssl:ssl_option()]}
                | {tcp_options, [gen_tcp:listen_option()]}| {udp_options, [gen_udp:option()]}
                | {dtls_options, [gen_udp:option() | ssl:ssl_option()]}).

%% 定义host类型,可能类型是inet:ip_address() 或者 string()
-type(host() :: inet:ip_address() | string()).
%% 定义listen_on() 可能类型是inet:port_number(),或者是ip和port的元组
-type(listen_on() :: inet:port_number() | {host(), inet:port_number()}).
%% 导出接口
-export_type([proto/0, transport/0, udp_transport/0, sock/0, sock_fun/0, mfargs/0, option/0, listen_on/0]).

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

%% @doc Start esockd application.
-spec(start() -> ok).
start() ->
    {ok, _} = application:ensure_all_started(esockd), ok.

%% @doc Open a TCP or SSL listener
%% Proto: 该参数是一个TCP服务的名称,
%% listen_on(): 是一个类型定义: -type(listen_on() :: inet:port_number() | {host(), inet:port_number()}). 服务的port或者服务器的ip和port
%% [option()]:这是一个数组参数,option()是一个类型定义:是关于TCP服务器的一些配置
%% mfargs类型定义:-type(mfargs() :: atom() | {atom(), atom()} | {module(), atom(), [term()]}).
%% 函数返回值:{ok,pid()} 或者{error,term()}
-spec(open(atom(), listen_on(), [option()], mfargs()) -> {ok, pid()} | {error, term()}).
%% 判断Proto是不是原子,Port是不是一个整数
open(Proto, Port, Opts, MFA) when is_atom(Proto), is_integer(Port) ->
%%    调用 esockd_sup 模块的start_listener 方法,比如:
%%    Opts = [{acceptors, 1}, {max_connections, 1024}, {tcp_options, [binary, {reuseaddr, true}]}].
%%    MFA = {echo_server, start_link, []},
%%    esockd:open(echo, 5000, Options, MFArgs).
	  esockd_sup:start_listener(Proto, Port, Opts, MFA);

%% 该方法跟上面的方法唯一区别就是第一个方法传递一个端口port就可以,该方法传递了一个元组{Host,Port}
open(Proto, {Host, Port}, Opts, MFA) when is_atom(Proto), is_integer(Port) ->
    %% 判断host和port,返回ip和port
    {IPAddr, _Port} = fixaddr({Host, Port}),
%%    通过ip获取和tcp配置的参数
    case proplists:get_value(ip, tcp_options(Opts)) of
%%        如果没有定义,返回ok
        undefined -> ok;
%%        如果有定义,返回ok
        IPAddr    -> ok;
%%        如果没有,就返回匹配错误
        Other     -> error({badmatch, Other})
    end,
%%    调用esockd_sup模块start_listener方法
	esockd_sup:start_listener(Proto, {IPAddr, Port}, Opts, MFA).

tcp_options(Opts) ->
    proplists:get_value(tcp_options, Opts, []).

%% 打开udp服务
%% Proto:属性名 Port:端口 Opts:配置 MFA:模块,方法,参数元组

open_udp(Proto, Port, Opts, MFA) ->
    esockd_sup:start_child(udp_child_spec(Proto, Port, Opts, MFA)).

udp_child_spec(Proto, Port, Opts, MFA) ->
    esockd_sup:udp_child_spec(Proto, fixaddr(Port), udp_options(Opts), MFA).

%% udp option参数获取
udp_options(Opts) ->
    proplists:get_value(udp_options, Opts, []).

%% 开启udp tls服务
open_dtls(Proto, ListenOn, Opts, MFA) ->
    esockd_sup:start_child(dtls_child_spec(Proto, ListenOn, Opts, MFA)).
%% udp tsl 子进程规范
dtls_child_spec(Proto, ListenOn, Opts, MFA) ->
    esockd_sup:dtls_child_spec(Proto, fixaddr(ListenOn), Opts, MFA).

%% @doc Child spec for a listener
-spec(child_spec(atom(), listen_on(), [option()], mfargs()) -> supervisor:child_spec()).
child_spec(Proto, ListenOn, Opts, MFA) when is_atom(Proto) ->
    esockd_sup:child_spec(Proto, fixaddr(ListenOn), Opts, MFA).

%%关闭监听
-spec(close({atom(), listen_on()}) -> ok | {error, term()}).
close({Proto, ListenOn}) when is_atom(Proto) ->
    close(Proto, ListenOn).

-spec(close(atom(), listen_on()) -> ok | {error, term()}).
close(Proto, ListenOn) when is_atom(Proto) ->
	esockd_sup:stop_listener(Proto, fixaddr(ListenOn)).

%% @doc Reopen the listener 从新打开监听
-spec(reopen({atom(), listen_on()}) -> {ok, pid()} | {error, term()}).
reopen({Proto, ListenOn}) when is_atom(Proto) ->
    reopen(Proto, ListenOn).

-spec(reopen(atom(), listen_on()) -> {ok, pid()} | {error, term()}).
reopen(Proto, ListenOn) when is_atom(Proto) ->
    esockd_sup:restart_listener(Proto, fixaddr(ListenOn)).

%% @doc Get listeners.
-spec(listeners() -> [{
   
   {atom(), listen_on()}, pid()}]).
listeners() -> esockd_sup:listeners().

%% @doc Get one listener.
-spec(listener({atom(), listen_on()}) -> pid() | undefined).
listener({Proto, ListenOn}) when is_atom(Proto) ->
    esockd_sup:listener({Proto, fixaddr(ListenOn)}).

%% @doc Get stats
-spec(get_stats({atom(), listen_on()}) -> [{atom(), non_neg_integer()}]).
get_stats({Proto, ListenOn}) when is_atom(Proto) ->
    esockd_server:get_stats({Proto, fixaddr(ListenOn)}).

%% @doc Get options
-spec(get_options({atom(), listen_on()}) -> undefined | pos_integer()).
get_options({Proto, ListenOn}) when is_atom(Proto) ->
    with_listener({Proto, ListenOn}, fun get_options/1);
get_options(LSup) when is_pid(LSup) ->
    esockd_listener:options(esockd_listener_sup:listener(LSup)).

%% 获取socket接收者数量
-spec(get_acceptors({atom(), listen_on()}) -> undefined | pos_integer()).
get_acceptors({Proto, ListenOn}) ->
    with_listener({Proto, ListenOn}, fun get_acceptors/1);
get_acceptors(LSup) when is_pid(LSup) ->
    AcceptorSup = esockd_listener_sup:acceptor_sup(LSup),
    esockd_acceptor_sup:count_acceptors(AcceptorSup).

%% 得到最大的连接数
-spec(get_max_connections({atom(), listen_on()} | pid()) -> undefined | pos_integer()).
get_max_connections({Proto, ListenOn}) when is_atom(Proto) ->
    with_listener({Proto, ListenOn}, fun get_max_connections/1);
get_max_connections(LSup) when is_pid(LSup) ->
    ConnSup = esockd_listener_sup:connection_sup(LSup),
    esockd_connection_sup:get_max_connections(ConnSup).

%% 调用接口设置应用最大的连接数
-spec(set_max_connections({atom(), listen_on()} | pid(), pos_integer()) -> undefined | pos_integer()).
set_max_connections({Proto, ListenOn}, MaxConns) when is_atom(Proto) ->
    with_listener({Proto, ListenOn}, fun set_max_connections/2, [MaxConns]);
set_max_connections(LSup, MaxConns) when is_pid(LSup) ->
    ConnSup = esockd_listener_sup:connection_sup(LSup),
    esockd_connection_sup:set_max_connections(ConnSup, MaxConns).

%% 获取当前系统的连接数
-spec(get_current_connections({atom(), listen_on()}) -> undefined | non_neg_integer()).
get_current_connections({Proto, ListenOn}) when is_atom(Proto) ->
    with_listener({Proto, ListenOn}, fun get_current_connections/1);
get_current_connections(LSup) when is_pid(LSup) ->
    ConnSup = esockd_listener_sup:connection_sup(LSup),
    esockd_connection_sup:count_connections(ConnSup).

%% @doc Get shutdown count
-spec(get_shutdown_count({atom(), listen_on()}) -> undefined | pos_integer()).
get_shutdown_count({Proto, ListenOn}) when is_atom(Proto) ->
    with_listener({Proto, ListenOn}, fun get_shutdown_count/1);
get_shutdown_count(LSup) when is_pid(LSup) ->
    ConnSup = esockd_listener_sup:connection_sup(LSup),
    esockd_connection_sup:get_shutdown_count(ConnSup).

%% @doc Get access rules
-spec(get_access_rules({atom(), listen_on()}) -> [esockd_access:rule()] | undefined).
get_access_rules({Proto, ListenOn}) when is_atom(Proto) ->
    with_listener({Proto, ListenOn}, fun get_access_rules/1);
get_access_rules(LSup) when is_pid(LSup) ->
    ConnSup = esockd_listener_sup:connection_sup(LSup),
    esockd_connection_sup:access_rules(ConnSup).

%% @doc Allow access address
-spec(allow({atom(), listen_on()}, all | esockd_cidr:cidr_string()) -> ok | {error, term()}).
allow({Proto, ListenOn}, CIDR) when is_atom(Proto) ->
    LSup = listener({Proto, ListenOn}),
    ConnSup = esockd_listener_sup:connection_sup(LSup),
    esockd_connection_sup:allow(ConnSup, CIDR).

%% @doc Deny access address
-spec(deny({atom(), listen_on()}, all | esockd_cidr:cidr_string()) -> ok | {error, term()}).
deny({Proto, ListenOn}, CIDR) when is_atom(Proto) ->
    LSup = listener({Proto, ListenOn}),
    ConnSup = esockd_listener_sup:connection_sup(LSup),
    esockd_connection_sup:deny(ConnSup, CIDR).

%% @doc Parse option.
parse_opt(Options) ->
    parse_opt(Options, []).
parse_opt([], Acc) ->
    lists:reverse(Acc);
parse_opt([{acceptors, I}|Opts], Acc) when is_integer(I) ->
    parse_opt(Opts, [{acceptors, I}|Acc]);
parse_opt([{max_connections, I}|Opts], Acc) when is_integer(I) ->
    parse_opt(Opts, [{max_connections, I}|Acc]);
parse_opt([{max_conn_rate, Limit}|Opts], Acc) when Limit > 0 ->
    parse_opt(Opts, [{max_conn_rate, {Limit, 1}}|Acc]);
parse_opt([{max_conn_rate, {Limit, Period}}|Opts], Acc) when Limit > 0, Period >0 ->
    parse_opt(Opts, [{max_conn_rate, {Limit, Period}}|Acc]);
parse_opt([{access_rules, Rules}|Opts], Acc) ->
    parse_opt(Opts, [{access_rules, Rules}|Acc]);
parse_opt([{shutdown, I}|Opts], Acc) when I == brutal_kill; I == infinity; is_integer(I) ->
    parse_opt(Opts, [{shutdown, I}|Acc]);
parse_opt([tune_buffer|Opts], Acc) ->
    parse_opt(Opts, [{tune_buffer, true}|Acc]);
parse_opt([{tune_buffer, I}|Opts], Acc) when is_boolean(I) ->
    parse_opt(Opts, [{tune_buffer, I}|Acc]);
parse_opt([proxy_protocol|Opts], Acc) ->
    parse_opt(Opts, [{proxy_protocol, true}|Acc]);
parse_opt([{proxy_protocol, I}|Opts], Acc) when is_boolean(I) ->
    parse_opt(Opts, [{proxy_protocol, I}|Acc]);
parse_opt([{proxy_protocol_timeout, Timeout}|Opts], Acc) when is_integer(Timeout) ->
    parse_opt(Opts, [{proxy_protocol_timeout, Timeout}|Acc]);
parse_opt([{ssl_options, L}|Opts], Acc) when is_list(L) ->
    parse_opt(Opts, [{ssl_options, L}|Acc]);
parse_opt([{tcp_options, L}|Opts], Acc) when is_list(L) ->
    parse_opt(Opts, [{tcp_options, L}|Acc]);
parse_opt([{udp_options, L}|Opts], Acc) when is_list(L) ->
    parse_opt(Opts, [{udp_options, L}|Acc]);
parse_opt([{dtls_options, L}|Opts], Acc) when is_list(L) ->
    parse_opt(Opts, [{dtls_options, L}|Acc]);
parse_opt([_|Opts], Acc) ->
    parse_opt(Opts, Acc).

%% @doc System 'ulimit -n'
-spec(ulimit() -> pos_integer()).
ulimit() ->
    proplists:get_value(max_fds, erlang:system_info(check_io)).

with_listener({Proto, ListenOn}, Fun) ->
    with_listener({Proto, ListenOn}, Fun, []).

with_listener({Proto, ListenOn}, Fun, Args) ->
    LSup = listener({Proto, ListenOn}),
    with_listener(LSup, Fun, Args);
with_listener(undefined, _Fun, _Args) ->
    undefined;
with_listener(LSup, Fun, Args) when is_pid(LSup) ->
    erlang:apply(Fun, [LSup | Args]).

-spec(to_string(listen_on()) -> string()).
%% 端口port integer 转 string
to_string(Port) when is_integer(Port) ->
    integer_to_list(Port);
%% 端口port和 ip地址合成一个ip:port的string
to_string({Addr, Port}) ->
    {IPAddr, Port} = fixaddr({Addr, Port}),
    inet:ntoa(IPAddr) ++ ":" ++ integer_to_list(Port).

%% 检查Port 是不是整数
fixaddr(Port) when is_integer(Port) ->
    Port;
%% 检查Addr是不是一个list,Port是不是整数
fixaddr({Addr, Port}) when is_list(Addr), is_integer(Port) ->
%%    解析地址,然后返回一个ip
    {ok, IPAddr} = inet:parse_address(Addr),
%%    返回元组{ip,port}
    {IPAddr, Port};
fixaddr({Addr, Port}) when is_tuple(Addr), is_integer(Port) ->
    case esockd_cidr:is_ipv6(Addr) or esockd_cidr:is_ipv4(Addr) of
        true  -> {Addr, Port};
        false -> error(invalid_ipaddr)
    end.

Cuando ok = esockd: start (). Se ejecuta en el shell, se llamará al método de este módulo:

%% @doc Start esockd application.
-spec(start() -> ok).
start() ->
    {ok, _} = application:ensure_all_started(esockd), ok.

Luego ejecute el método en el módulo esockd_app.erl:

start(_StartType, _StartArgs) ->
    io:format(" esockd_app start ~n"),
    esockd_sup:start_link().

El siguiente artículo presenta el módulo esockd_sup, que actúa como escucha raíz de la aplicación.

Supongo que te gusta

Origin blog.csdn.net/qq513036862/article/details/87968708
Recomendado
Clasificación