接上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. 【RabbitMQ】工作模式介绍

    一.前言 之前,笔者写过< CentOS 7.2 安装 RabbitMQ> 这篇文章,今天整理一下 RabbitMQ 相关的笔记便于以后复习. 二.模式介绍 在 RabbitMQ 官网上提 ...

  2. RNN Train和Test Mismatch

    李宏毅深度学习 https://www.bilibili.com/video/av9770302/?p=8 在看RNN的时候,你是不是也会觉得有些奇怪, Train的过程中, 是把训练集中的结果作为下 ...

  3. SQL UPDATE嵌套使用

    遇见的问题 1.更新语句,更新字段 UPDATE number SET sumab = 2 WHERE id =1 求和 SELECT SUM(num_a+num_b)FROM number WHER ...

  4. 《nginx - 基本操作/配置》

    一:基本操作 - 开启 Nginx nginx -c nginx.conf -  Nginx 的平滑重启 kill -HUP nginx主进程号(平滑重启) -  停止 Nginx * Kill -Q ...

  5. ADB命令(全)

    当熟悉Shell命令的人使用adb是没有任何难度的,因为adb中绝大多少命令都是引用的shell命令 以下命令进行归类,红色部分字体是测试人员常用的adb命令 基础命令 1.启动adb服务 adb s ...

  6. VUE中的v-show和v-if

    v-show="判断条件" 显示或隐藏 v-show占位置 v-if 隐藏(不占位置)

  7. linux下目录的作用

    FHS针对目录树架构仅定义出三层目录底下应该放置什么数据而已,分别是底下这三个目录的定义: / (root, 根目录):与开机系统有关: /usr (unix software resource):与 ...

  8. Linux hostname设置,静态ip设置,hostname与静态ip相互映射

    1,hostname设置 永久设置: centos 7 下,切换到root 用户 vi /etc/hostname 输入要修改的hostname centos 6或者其他linux 系统,可能在/et ...

  9. 千行代码入门Python

    这个是从网上找到的一份快速入门python的极简教程,大概一千行左右,个人觉得不错,特此收藏以备后用. # _*_ coding: utf-8 _*_ """类型和运算- ...

  10. 分布式系统Paxos算法

    转载 原地址:https://www.jdon.com/artichect/paxos.html 主要加一个对应场景,如:Spring Cloud 的 Consul 集权之间的通信,其实是Raft算法 ...