ranch源码解读

现在Erlang网络库基本上都是使用ranch,今天我们就看看源码怎么实现的
首先,在我们的印象里,应该只要listen和accept,然后使用socket收发信息就可以了,但是为了让程序更加健壮,我们必须合理的安排哪些是supervisor,哪些是worker,废话不多说show me code
1、启动ranch,看ranch_app:start/2

start(_, _) ->
    _ = consider_profiling(),  
    ranch_sup:start_link().

看ranch_sup:start_link().

-module(ranch_sup).
-behaviour(supervisor).

-export([start_link/0]).
-export([init/1]).

-spec start_link() -> {ok, pid()}.
start_link() ->
    supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init([]) ->
    Intensity = case application:get_env(ranch_sup_intensity) of
        {ok, Value1} -> Value1;
        undefined -> 1
    end,
    Period = case application:get_env(ranch_sup_period) of
        {ok, Value2} -> Value2;
        undefined -> 5
    end,
    ranch_server = ets:new(ranch_server, [
        ordered_set, public, named_table]),
    Procs = [
        {ranch_server, {ranch_server, start_link, []},
            permanent, 5000, worker, [ranch_server]}
    ],
    {ok, {{one_for_one, Intensity, Period}, Procs}}.

读取配置,启动 ranch_server(主要是设置,获取服务器的一些配置和资源情况,使用ETS)

到这里,ranch就启动完毕了,启动了一个ranch_sup,下面挂了个ranch_server

2、再看如何启动服务端的listen和accept

看ranch模块

-spec start_listener(ref(), module(), opts(), module(), any())
    -> supervisor:startchild_ret().
start_listener(Ref, Transport, TransOpts0, Protocol, ProtoOpts)
        when is_atom(Transport), is_atom(Protocol) ->
    TransOpts = normalize_opts(TransOpts0),
    _ = code:ensure_loaded(Transport),
    case erlang:function_exported(Transport, name, 0) of
        false ->
            {error, badarg};
        true ->
            Res = supervisor:start_child(ranch_sup, child_spec(Ref,
                    Transport, TransOpts, Protocol, ProtoOpts)),
            Socket = maps:get(socket, TransOpts, undefined),
            case Res of
                {ok, Pid} when Socket =/= undefined ->
                    %% Give ownership of the socket to ranch_acceptors_sup
                    %% to make sure the socket stays open as long as the
                    %% listener is alive. If the socket closes however there
                    %% will be no way to recover because we don't know how
                    %% to open it again.
                    Children = supervisor:which_children(Pid),
                    {_, AcceptorsSup, _, _}
                        = lists:keyfind(ranch_acceptors_sup, 1, Children),
                    Transport:controlling_process(Socket, AcceptorsSup);
                _ ->
                    ok
            end,
            maybe_started(Res)
    end.

-spec start_listener(ref(), non_neg_integer(), module(), opts(), module(), any())
    -> supervisor:startchild_ret().
start_listener(Ref, NumAcceptors, Transport, TransOpts0, Protocol, ProtoOpts)
        when is_integer(NumAcceptors), is_atom(Transport), is_atom(Protocol) ->
    TransOpts = normalize_opts(TransOpts0),
    start_listener(Ref, Transport, TransOpts#{num_acceptors => NumAcceptors},
        Protocol, ProtoOpts).

Ref, %% ets表中的key,相当于启动服务的key信息
NumAcceptors, %% 可接收最大的Accept数量
Transport, %% 处理 TCP/IP sockets的接口模块 一般使用ranch_tcp
TransOpts0, %% TCP/IP sockets的参数列表
Protocol, %% 回调模块
ProtoOpts %% 回调参数列表

主要看这行

Res = supervisor:start_child(ranch_sup, child_spec(Ref,
                    Transport, TransOpts, Protocol, ProtoOpts)),
-spec child_spec(ref(), module(), opts(), module(), any())
    -> supervisor:child_spec().
child_spec(Ref, Transport, TransOpts0, Protocol, ProtoOpts) ->
    TransOpts = normalize_opts(TransOpts0),
    {{ranch_listener_sup, Ref}, {ranch_listener_sup, start_link, [
        Ref, Transport, TransOpts, Protocol, ProtoOpts
    ]}, permanent, infinity, supervisor, [ranch_listener_sup]}.

-spec child_spec(ref(), non_neg_integer(), module(), opts(), module(), any())
    -> supervisor:child_spec().
child_spec(Ref, NumAcceptors, Transport, TransOpts0, Protocol, ProtoOpts)
        when is_integer(NumAcceptors), is_atom(Transport), is_atom(Protocol) ->
    TransOpts = normalize_opts(TransOpts0),
    child_spec(Ref, Transport, TransOpts#{num_acceptors => NumAcceptors},
        Protocol, ProtoOpts).

这代码主要是在ranch_sup启动ranch_listener_sup这个supervisor
那就看ranch_listener_sup代码

-module(ranch_listener_sup).
-behaviour(supervisor).

-export([start_link/5]).
-export([init/1]).

-spec start_link(ranch:ref(), module(), any(), module(), any())
    -> {ok, pid()}.
start_link(Ref, Transport, TransOpts, Protocol, ProtoOpts) ->
    MaxConns = maps:get(max_connections, TransOpts, 1024),
    ranch_server:set_new_listener_opts(Ref, MaxConns, TransOpts, ProtoOpts,
        [Ref, Transport, TransOpts, Protocol, ProtoOpts]),
    supervisor:start_link(?MODULE, {
        Ref, Transport, Protocol
    }).

init({Ref, Transport, Protocol}) ->
    ok = ranch_server:set_listener_sup(Ref, self()),
    ChildSpecs = [
        {ranch_conns_sup, {ranch_conns_sup, start_link,
                [Ref, Transport, Protocol]},
            permanent, infinity, supervisor, [ranch_conns_sup]},
        {ranch_acceptors_sup, {ranch_acceptors_sup, start_link,
                [Ref, Transport]},
            permanent, infinity, supervisor, [ranch_acceptors_sup]}
    ],
    {ok, {{rest_for_one, 1, 5}, ChildSpecs}}.

里面就是启动了ranch_conns_sup 和 ranch_acceptors_sup
看下 ranch_conns_sup 这个监督进程代码

-spec start_link(ranch:ref(), module(), module()) -> {ok, pid()}.
start_link(Ref, Transport, Protocol) ->
    proc_lib:start_link(?MODULE, init,
        [self(), Ref, Transport, Protocol]).



loop(State=#state{parent=Parent, ref=Ref, conn_type=ConnType,
        transport=Transport, protocol=Protocol, opts=Opts,
        max_conns=MaxConns, logger=Logger}, CurConns, NbChildren, Sleepers) ->
    receive
        {?MODULE, start_protocol, To, Socket} ->
            try Protocol:start_link(Ref, Socket, Transport, Opts) of
                {ok, Pid} ->
                    handshake(State, CurConns, NbChildren, Sleepers, To, Socket, Pid, Pid);
                {ok, SupPid, ProtocolPid} when ConnType =:= supervisor ->
                    handshake(State, CurConns, NbChildren, Sleepers, To, Socket, SupPid, ProtocolPid);
                Ret ->
                    To ! self(),
                    ranch:log(error,
                        "Ranch listener ~p connection process start failure; "
                        "~p:start_link/4 returned: ~999999p~n",
                        [Ref, Protocol, Ret], Logger),
                    Transport:close(Socket),
                    loop(State, CurConns, NbChildren, Sleepers)
            catch Class:Reason ->
                To ! self(),
                ranch:log(error,
                    "Ranch listener ~p connection process start failure; "
                    "~p:start_link/4 crashed with reason: ~p:~999999p~n",
                    [Ref, Protocol, Class, Reason], Logger),
                loop(State, CurConns, NbChildren, Sleepers)
            end;
        {?MODULE, active_connections, To, Tag} ->
            To ! {Tag, CurConns},
            loop(State, CurConns, NbChildren, Sleepers);
        %% Remove a connection from the count of connections.
        {remove_connection, Ref, Pid} ->
            case put(Pid, removed) of
                active ->
                    loop(State, CurConns - 1, NbChildren, Sleepers);
                remove ->
                    loop(State, CurConns, NbChildren, Sleepers);
                undefined ->
                    _ = erase(Pid),
                    loop(State, CurConns, NbChildren, Sleepers)
            end;
        %% Upgrade the max number of connections allowed concurrently.
        %% We resume all sleeping acceptors if this number increases.
        {set_max_conns, MaxConns2} when MaxConns2 > MaxConns ->
            _ = [To ! self() || To <- Sleepers],
            loop(State#state{max_conns=MaxConns2},
                CurConns, NbChildren, []);
        {set_max_conns, MaxConns2} ->
            loop(State#state{max_conns=MaxConns2},
                CurConns, NbChildren, Sleepers);
        %% Upgrade the protocol options.
        {set_opts, Opts2} ->
            loop(State#state{opts=Opts2},
                CurConns, NbChildren, Sleepers);
        {'EXIT', Parent, Reason} ->
            terminate(State, Reason, NbChildren);
        {'EXIT', Pid, Reason} when Sleepers =:= [] ->
            case erase(Pid) of
                active ->
                    report_error(Logger, Ref, Protocol, Pid, Reason),
                    loop(State, CurConns - 1, NbChildren - 1, Sleepers);
                removed ->
                    report_error(Logger, Ref, Protocol, Pid, Reason),
                    loop(State, CurConns, NbChildren - 1, Sleepers);
                undefined ->
                    loop(State, CurConns, NbChildren, Sleepers)
            end;
        %% Resume a sleeping acceptor if needed.
        {'EXIT', Pid, Reason} ->
            case erase(Pid) of
                active when CurConns > MaxConns ->
                    report_error(Logger, Ref, Protocol, Pid, Reason),
                    loop(State, CurConns - 1, NbChildren - 1, Sleepers);
                active ->
                    report_error(Logger, Ref, Protocol, Pid, Reason),
                    [To|Sleepers2] = Sleepers,
                    To ! self(),
                    loop(State, CurConns - 1, NbChildren - 1, Sleepers2);
                removed ->
                    report_error(Logger, Ref, Protocol, Pid, Reason),
                    loop(State, CurConns, NbChildren - 1, Sleepers);
                undefined ->
                    loop(State, CurConns, NbChildren, Sleepers)
            end;
        {system, From, Request} ->
            sys:handle_system_msg(Request, From, Parent, ?MODULE, [],
                {State, CurConns, NbChildren, Sleepers});
        %% Calls from the supervisor module.
        {'$gen_call', {To, Tag}, which_children} ->
            Children = [{Protocol, Pid, ConnType, [Protocol]}
                || {Pid, Type} <- get(),
                Type =:= active orelse Type =:= removed],
            To ! {Tag, Children},
            loop(State, CurConns, NbChildren, Sleepers);
        {'$gen_call', {To, Tag}, count_children} ->
            Counts = case ConnType of
                worker -> [{supervisors, 0}, {workers, NbChildren}];
                supervisor -> [{supervisors, NbChildren}, {workers, 0}]
            end,
            Counts2 = [{specs, 1}, {active, NbChildren}|Counts],
            To ! {Tag, Counts2},
            loop(State, CurConns, NbChildren, Sleepers);
        {'$gen_call', {To, Tag}, _} ->
            To ! {Tag, {error, ?MODULE}},
            loop(State, CurConns, NbChildren, Sleepers);
        Msg ->
            ranch:log(error,
                "Ranch listener ~p received unexpected message ~p~n",
                [Ref, Msg], Logger),
            loop(State, CurConns, NbChildren, Sleepers)
    end.

启动完,在loop函数等待接收数据

看启动 ranch_acceptors_sup 代码

start_link(Ref, Transport) ->
    supervisor:start_link(?MODULE, [Ref, Transport]).

init([Ref, Transport]) ->
    ConnsSup = ranch_server:get_connections_sup(Ref),
    TransOpts = ranch_server:get_transport_options(Ref),
    NumAcceptors = maps:get(num_acceptors, TransOpts, 10),
    Logger = maps:get(logger, TransOpts, error_logger),
    LSocket = case maps:get(socket, TransOpts, undefined) of
        undefined ->
            SocketOpts = maps:get(socket_opts, TransOpts, []),
            %% We temporarily put the logger in the process dictionary
            %% so that it can be used from ranch:filter_options. The
            %% interface as it currently is does not allow passing it
            %% down otherwise.
            put(logger, Logger),
            case Transport:listen(SocketOpts) of
                {ok, Socket} ->
                    erase(logger),
                    Socket;
                {error, Reason} ->
                    listen_error(Ref, Transport, SocketOpts, Reason, Logger)
            end;
        Socket ->
            Socket
    end,
    {ok, Addr} = Transport:sockname(LSocket),
    ranch_server:set_addr(Ref, Addr),
    Procs = [
        {{acceptor, self(), N}, {ranch_acceptor, start_link, [
            LSocket, Transport, Logger, ConnsSup
        ]}, permanent, brutal_kill, worker, []}
            || N <- lists:seq(1, NumAcceptors)],
    {ok, {{one_for_one, 1, 5}, Procs}}.

这段代码就是启动监听,产生监听socket,然后启动NumAcceptors个ranch_acceptor进程

start_link(LSocket, Transport, Logger, ConnsSup) ->
    Pid = spawn_link(?MODULE, loop, [LSocket, Transport, Logger, ConnsSup]),
    {ok, Pid}.

-spec loop(inet:socket(), module(), module(), pid()) -> no_return().
loop(LSocket, Transport, Logger, ConnsSup) ->
    _ = case Transport:accept(LSocket, infinity) of
        {ok, CSocket} ->
            case Transport:controlling_process(CSocket, ConnsSup) of
                ok ->
                    %% This call will not return until process has been started
                    %% AND we are below the maximum number of connections.
                    ranch_conns_sup:start_protocol(ConnsSup, CSocket);
                {error, _} ->
                    Transport:close(CSocket)
            end;
        %% Reduce the accept rate if we run out of file descriptors.
        %% We can't accept anymore anyway, so we might as well wait
        %% a little for the situation to resolve itself.
        {error, emfile} ->
            ranch:log(warning,
                "Ranch acceptor reducing accept rate: out of file descriptors~n",
                [], Logger),
            receive after 100 -> ok end;
        %% Exit if the listening socket got closed.
        {error, closed} ->
            exit(closed);
        %% Continue otherwise.
        {error, _} ->
            ok
    end,
    flush(Logger),
    ?MODULE:loop(LSocket, Transport, Logger, ConnsSup).

当有client连接过来,产生csocket,并把这个socket转交给ranch_conns_sup进程
ranch_conns_sup:start_protocol(ConnsSup, CSocket);

-spec start_protocol(pid(), inet:socket()) -> ok.
start_protocol(SupPid, Socket) ->
    SupPid ! {?MODULE, start_protocol, self(), Socket},
    receive SupPid -> ok end.

这是向ranch_conns_sup进程发送 {?MODULE, start_protocol, self(), Socket}信息
然后看下收到消息的逻辑

loop(State=#state{parent=Parent, ref=Ref, conn_type=ConnType,
        transport=Transport, protocol=Protocol, opts=Opts,
        max_conns=MaxConns, logger=Logger}, CurConns, NbChildren, Sleepers) ->
    receive
        {?MODULE, start_protocol, To, Socket} ->
            try Protocol:start_link(Ref, Socket, Transport, Opts) of
                {ok, Pid} ->
                    handshake(State, CurConns, NbChildren, Sleepers, To, Socket, Pid, Pid);

这里就是启动回调模块的进程,


handshake(State=#state{ref=Ref, transport=Transport, handshake_timeout=HandshakeTimeout,
        max_conns=MaxConns}, CurConns, NbChildren, Sleepers, To, Socket, SupPid, ProtocolPid) ->
    case Transport:controlling_process(Socket, ProtocolPid) of
        ok ->
            ProtocolPid ! {handshake, Ref, Transport, Socket, HandshakeTimeout},
            put(SupPid, active),
            CurConns2 = CurConns + 1,
            if CurConns2 < MaxConns ->
                    To ! self(),
                    loop(State, CurConns2, NbChildren + 1, Sleepers);
                true ->
                    loop(State, CurConns2, NbChildren + 1, [To|Sleepers])
            end;
        {error, _} ->
            Transport:close(Socket),
            %% Only kill the supervised pid, because the connection's pid,
            %% when different, is supposed to be sitting under it and linked.
            exit(SupPid, kill),
            To ! self(),
            loop(State, CurConns, NbChildren, Sleepers)
    end.

这段就是把csocket转交给回调的进程,
注意下,有可能controlling_process失败,所以需要在回调进程调用
ranch:handshake,然后ProtocolPid ! {handshake, Ref, Transport, Socket, HandshakeTimeout},
然后判断连接数,是否阻塞


image.png

有小伙伴问:
为什么需要从ranch_acceptor转到ranch_conns_sup再转到回调进程
1、当连接后,立马有大量的数据发过来,所有数据都堵塞在ranch_acceptor进程
2、需要判断连接数是否超出最大值

你可能感兴趣的:(ranch源码解读)