接上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/6启动init后直接使用了loop来循环,

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

loop后等待消息(我们主要看start_protocol消息),loop函数根据start_protocol消息启动Protocol:start_link/4(用户编写的应用模块,这里表示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/1开始监听端口

启动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/2发送{?MODULE, start_protocol, self(), Socket}给ConnsSup,

-module(ranch_conns_sup).

%% API.
-export([start_link/6]).
-export([start_protocol/2]).
-export([active_connections/1]). %%.......省略若干行 -spec start_protocol(pid(), inet:socket()) -> ok.
start_protocol(SupPid, Socket) ->
SupPid ! {?MODULE, start_protocol, self(), Socket},
receive SupPid -> ok end. %%.......省略若干行

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

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

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

  1. ranch 源码分析(完)

    接上 ranch 源码分析(三) 在上一次,根据ranch源码把大概流程理了一遍,下面我们将一些细节解释一下. ranch只是一个服务的框架,它提供了传输层协议代码(ranch_tcp 和ranch_ ...

  2. tomcat源码分析(三)一次http请求的旅行-从Socket说起

    p { margin-bottom: 0.25cm; line-height: 120% } tomcat源码分析(三)一次http请求的旅行 在http请求旅行之前,我们先来准备下我们所需要的工具. ...

  3. ranch 源码分析(二)

    接上ranch 源码分析(一) 上次讲到了ranch.erl的start_listener函数,下面我们详细分析下这个函数 -module(ranch). %...... 省略若干行 -spec st ...

  4. 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入

    使用react全家桶制作博客后台管理系统   前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...

  5. ABP源码分析三:ABP Module

    Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ...

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

    这个模块封装了Automapper,使其更易于使用. 下图描述了改模块涉及的所有类之间的关系. AutoMapAttribute,AutoMapFromAttribute和AutoMapToAttri ...

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

    ABP.Web模块并不复杂,主要完成ABP系统的初始化和一些基础功能的实现. AbpWebApplication : 继承自ASP.Net的HttpApplication类,主要完成下面三件事一,在A ...

  8. ABP源码分析三十四:ABP.Web.Mvc

    ABP.Web.Mvc模块主要完成两个任务: 第一,通过自定义的AbpController抽象基类封装ABP核心模块中的功能,以便利的方式提供给我们创建controller使用. 第二,一些常见的基础 ...

  9. ABP源码分析三十五:ABP中动态WebAPI原理解析

    动态WebAPI应该算是ABP中最Magic的功能之一了吧.开发人员无须定义继承自ApiController的类,只须重用Application Service中的类就可以对外提供WebAPI的功能, ...

随机推荐

  1. 关于Oracle归档的一些操作

    1.查看日志模式archive log list,或者select name,log_mode from v$database; 2.数据库非归档模式改为归档模式: 关闭数据库:shutdown im ...

  2. python pynput监听键盘

    """小白随笔,大佬勿喷""" #键盘输入 from pynput.keyboard import Key,Controller,Liste ...

  3. python中接入支付宝当面付

    准备 由于正式环境需要商户信息,所以这里使用支付宝提供的沙箱环境.切换到正式环境后只需稍改配置. 1.点击进入蚂蚁金服平台官网. 2.如下图选择:开发者中心->开发服务下的研发服务->沙箱 ...

  4. slf4j日志使用

    scala中 trait LogSupport { protected val log = LoggerFactory.getLogger(this.getClass) } 需要要到的类 extend ...

  5. 【Go语言】基本的语法

    昨天花了几个小时的时间把Go的语法过了一遍,发现Go语言的语法核心和大部分编程语言的规则还是挺相近的,差别的就是不同的书写规范.还有就是前天安装VScode编译器那个插件把人弄得恶心了,总是安装不成功 ...

  6. Tomcat服务安全加固

    Tomcat服务默认启用了管理后台功能,使用该后台可直接上传 war 文件包对站点进行部署和管理.由于运维人员的疏忽,可能导致管理后台存在空口令或者弱口令的漏洞,使得黑客或者不法分子可以利用该漏洞直接 ...

  7. cocos2d-x JS 定时器暂停方法

    this.scheduleOnce(function(){ this.addChild(Menugobtn);//要暂停执行的代码 }, 10);

  8. jenkins配置工程目录-启动case

    1.我们在python里面编辑的脚本可以正常跑,但是在cmd里面跑就不行了,找不到自己定义的方法模块,这个时候我们要搞个环境变量 name  :   PYTHONPATH   val : 工程目录路劲 ...

  9. mint-ui Toast icon 图标

    Toast({ message: '修改成功', iconClass: 'fa fa-check fa-5x' }); Toast({ message: '修改失败', iconClass: 'fa ...

  10. python_函数式编程

    函数式编程是一种编程范式 (而面向过程编程和面向对象编程也都是编程范式).在面向过程编程中,我们见到过函数(function):在面向对象编程中,我们见过对象(object).函数和对象的根本目的是以 ...