ranch 源码分析(三)

接上ranch 源码分析(二)

上次讲到了ranch_conns_sup和ranch_acceptors_sup这2个ranch的核心模块,我们接着分析

首先查看ranch_conns_sup.erl

-module(ranch_conns_sup).

%% API.
-export([start_link/6]).
-export([start_protocol/2]).
-export([active_connections/1]).

%...... 省略若干行

%% API.

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

%...... 省略若干行

%% Supervisor internals.

-spec init(pid(), ranch:ref(), conn_type(), shutdown(),
    module(), timeout(), module()) -> no_return().
init(Parent, Ref, ConnType, Shutdown, Transport, AckTimeout, Protocol) ->
    process_flag(trap_exit, true),
    ok = ranch_server:set_connections_sup(Ref, self()),
    MaxConns = ranch_server:get_max_connections(Ref),
    Opts = ranch_server:get_protocol_options(Ref),
    ok = proc_lib:init_ack(Parent, {ok, self()}),
    loop(#state{parent=Parent, ref=Ref, conn_type=ConnType,
        shutdown=Shutdown, transport=Transport, protocol=Protocol,
        opts=Opts, ack_timeout=AckTimeout, max_conns=MaxConns}, 0, 0, []).

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

%...... 省略若干行

shoot(State=#state{ref=Ref, transport=Transport, ack_timeout=AckTimeout, max_conns=MaxConns},
        CurConns, NbChildren, Sleepers, To, Socket, SupPid, ProtocolPid) ->
    case Transport:controlling_process(Socket, ProtocolPid) of
        ok ->
            ProtocolPid ! {shoot, Ref, Transport, Socket, AckTimeout},
            put(SupPid, true),
            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),
            loop(State, CurConns, NbChildren, Sleepers)
    end.

%...... 省略若干行

可以看到ranch_conns_sup不是一个典型的gen_tcp模块,

start_link 启动init后直接使用了loop来循环,

init函数主要是获取了一些参数,

loop后等待消息(我们主要看start_protocol消息),loop函数根据start_protocol消息启动Protocol:start_link(用户编写的应用模块,这里表示echo_protocol),后面注意谁发这个消息给它

启动正常后,记录下ProtocolPid

继续查看ranch_acceptors_sup.erl

-module(ranch_acceptors_sup).
-behaviour(supervisor).

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

-spec start_link(ranch:ref(), non_neg_integer(), module(), any())
    -> {ok, pid()}.
start_link(Ref, NbAcceptors, Transport, TransOpts) ->
    supervisor:start_link(?MODULE, [Ref, NbAcceptors, Transport, TransOpts]).

init([Ref, NbAcceptors, Transport, TransOpts]) ->
    ConnsSup = ranch_server:get_connections_sup(Ref),
    LSocket = case proplists:get_value(socket, TransOpts) of
        undefined ->
            TransOpts2 = proplists:delete(ack_timeout,
                proplists:delete(connection_type,
                proplists:delete(max_connections,
                proplists:delete(shutdown,
                proplists:delete(socket, TransOpts))))),
            case Transport:listen(TransOpts2) of
                {ok, Socket} -> Socket;
                {error, Reason} -> listen_error(Ref, Transport, TransOpts2, Reason)
            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, ConnsSup
        ]}, permanent, brutal_kill, worker, []}
            || N <- lists:seq(1, NbAcceptors)],
    {ok, {{one_for_one, 1, 5}, Procs}}.

-spec listen_error(any(), module(), any(), atom()) -> no_return().
listen_error(Ref, Transport, TransOpts2, Reason) ->
    error_logger:error_msg(
        "Failed to start Ranch listener ~p in ~p:listen(~p) for reason ~p (~s)~n",
        [Ref, Transport, TransOpts2, Reason, inet:format_error(Reason)]),
    exit({listen_error, Ref, Reason}).

这里进行最主要的操作有

打开端口Transport:listen 开始监听端口

启动NbAcceptors个ranch_accetor:start_link进程等待连接,

接下来就是查看ranch_acceptor.erl

-module(ranch_acceptor).

-export([start_link/3]).
-export([loop/3]).

-spec start_link(inet:socket(), module(), pid())
    -> {ok, pid()}.
start_link(LSocket, Transport, ConnsSup) ->
    Pid = spawn_link(?MODULE, loop, [LSocket, Transport, ConnsSup]),
    {ok, Pid}.

-spec loop(inet:socket(), module(), pid()) -> no_return().
loop(LSocket, Transport, 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} ->
            receive after 100 -> ok end;
        %% We want to crash if the listening socket got closed.
        {error, Reason} when Reason =/= closed ->
            ok
    end,
    flush(),
    ?MODULE:loop(LSocket, Transport, ConnsSup).

flush() ->
    receive Msg ->
        error_logger:error_msg(
            "Ranch acceptor received unexpected message: ~p~n",
            [Msg]),
        flush()
    after 0 ->
        ok
    end.

当客户端端连接,accept返回ok,ranch_conns_sup:start_protocol发送{?MODULE, start_protocol, self(), Socket}给ConnsSup,

启动Protocol:start_link详细,参考上面的ranch_conns_sup分析

好,这个时候基本ranch的大致结构就分析出来了,还有一些其他的细节留以后慢慢添加。

时间: 2024-07-29 10:47:16

ranch 源码分析(三)的相关文章

ranch 源码分析(完)

接上 ranch 源码分析(三) 在上一次,根据ranch源码把大概流程理了一遍,下面我们将一些细节解释一下. ranch只是一个服务的框架,它提供了传输层协议代码(ranch_tcp 和ranch_ssl), 大概可以看看这2个的源码,其实就是gen_tcp和gen_ssl的封装. 它做的工作可以分成 1.根据port配置开启一个端口的linsten,(具体见ranch_acceptors_sup.erl代码) 2.然后开启N个(根据设置)进程accpet等待client的连接, (具体见ra

Nouveau源码分析(三):NVIDIA设备初始化之nouveau_drm_probe

Nouveau源码分析(三) 向DRM注册了Nouveau驱动之后,内核中的PCI模块就会扫描所有没有对应驱动的设备,然后和nouveau_drm_pci_table对照. 对于匹配的设备,PCI模块就调用对应的probe函数,也就是nouveau_drm_probe. // /drivers/gpu/drm/nouveau/nouveau_drm.c 281 static int nouveau_drm_probe(struct pci_dev *pdev, 282 const struct

[Android]Fragment源码分析(三) 事务

Fragment管理中,不得不谈到的就是它的事务管理,它的事务管理写的非常的出彩.我们先引入一个简单常用的Fragment事务管理代码片段: FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction(); ft.add(R.id.fragmentContainer, fragment, "tag"); ft.addToBackStack("<span style="fo

baksmali和smali源码分析(三)

baksmali 的源码分析 在baksmali进行源码分析之前,需要读者掌握一条主线,因为本身笔者只是由于项目需要用到这套源码,在工作之余的时间里面来进行学习也没有时间和精力熟读源码的每个文件每个方法,但是依据这条主线,至少能够猜出并且猜对baksmali里面的源码的文件大概的作用是什么,这样在修改问题和移植的时候才能做到游刃有余. 这条主线是,baksmali其实只是利用了dexlib2提供的接口,将dex文件读入到一块内存中,这块内存或者说数据结构开辟的大小是跟输入的dex文件相关的,而这

横屏小游戏--萝莉快跑源码分析三

主角出场: 初始化主角 hero = new GameObjHero(); hero->setScale(0.5); hero->setPosition(ccp(100,160)); hero->setVisible(false); addChild(hero,1); 进入GameObjHero类ccp文件 创建主角及动作 this->setContentSize(CCSizeMake(85,90)); //接收触摸事件 CCDirector* pDirector = CCDire

哇!板球 源码分析三

守门员出场 守门员出场,每个守门员是从屏幕的右侧中间的位置随机方向向左侧移动 FielderSprite* fielderSprite1 = FielderSprite::create("pic/fielder.png"); //守门员精灵初始位置为右侧中间位置 fielderSprite1->setPosition(ccp(GOALKEEPER_X, GOALKEEPER_Y)); fielderSprite1->setAnchorPoint(ccp(0.5, 0.5))

ranch 源码分析(二)

接上ranch 源码分析(一) 上次讲到了ranch.erl的start_listener函数,下面我们详细分析下这个函数 -module(ranch). %...... 省略若干行 -spec start_listener(ref(), non_neg_integer(), module(), any(), module(), any()) -> supervisor:startchild_ret(). start_listener(Ref, NbAcceptors, Transport, T

ABP源码分析三十三:ABP.Web

ABP.Web模块并不复杂,主要完成ABP系统的初始化和一些基础功能的实现. AbpWebApplication : 继承自ASP.Net的HttpApplication类,主要完成下面三件事一,在Application_Start完成AbpBootstrapper的初始化.整个ABP系统的初始化就是通过AbpBootstrapper完成初始化的.二,在Application_BeginRequest设置根据request或cookie中的Culture信息,完成当前工作线程的CurrentCu

ABP源码分析三十一:ABP.AutoMapper

这个模块封装了Automapper,使其更易于使用. 下图描述了改模块涉及的所有类之间的关系. AutoMapAttribute,AutoMapFromAttribute和AutoMapToAttribute:这三个attribute用于标注一个类到另外一个类的map方向. AutoMapperHelper: 通过调用Automapper的API,根据类的AutoMap的特性完成类型之间的Map. AbpAutoMapperModule: 1. 查找项目中所有标注了AutoMap特性的类型,并完