Netty核心原理与基础实战(二)——详解Bootstrap
接上篇:Netty核心原理与基础实战(一)
1 Bootstrap基础概念
Bootstrap类是Netty提供的一个便利的工厂类,可以通过它来完成Netty的客户端或服务端的Netty组件的组装,以及Netty程序的初始化和启动执行。Netty的官方解释是:完全可以不用Bootstrap类,可以一点点去手动创建通道、完成各种设置和启动注册到EventLoop反应器,然后开始事件的轮询和处理,但是这个过程会非常麻烦。通常情况下,使用这个便利的Bootstrap工具类的效率会更高。
在Netty中有两个引导类,分别用于服务器和客户端,如下图:
这两个引导类仅使用的地方不同,它们大致的配置和使用方法都是相同的。下面以ServerBootStrap类作为重点介绍对象。
在介绍 ServerBootStrap 的服务器启动流程之前,首先介绍一下涉及的两个基础概念:父子通道、EventLoopGroup(事件轮询线程组)。
1.1 父子通道
在NEtty中,每一个NioSocketChannel通道所封装的都是Java NIO通道,再往下就对应到了操作系统底层的socket文件描述符。理论上来说,操作系统底层的socket文件描述符分两类:
1、连接监听类型。连接监听类型的socket描述符处于服务端,负责接收客户端的套接字连接;在服务端,一个“连接监听类型”的socket描述符可以接受成千上万的传输类的socket文件描述符。
2、数据传输类型。数据传输类型的socket描述符负责传输数据。同一个TCP的socket传输链路在服务器和客户端都分别会有一个与之相对应的数据传输类型的socket文件描述符。
在NEtty中,异步非阻塞的服务端监听通道NioServerSocketChannel所封装的Linux底层的文件描述符是“连接监听类型”的socket描述符;异步非阻塞的传输通道NioSocketChannel所封装的Linux的文件描述符是“数据传输类型”的socket描述符。
在NEtty中,将有接收关系的监听通道和传输通道叫做父子通道。其中,负责服务器链接监听和接受的监听通道叫父通道,对应于每一个接收到的传输类型通道叫子通道。
1.2 EventLoopGroup
前面介绍Reactor模式的具体实现时,分为单线程实现版本和多线程实现版本。NEtty中的Reactor模式实现的是多线程版本。
实际上,在NEtty中一个EventLoop相当于一个子反应器(SubReactor),一个NioEventLoop子反应器拥有了一个事件轮询线程,同时拥有一个Java NIO选择器。
NEtty是如何实现多线程版本的Reactor模式呢?是使用EventLoopGroup(事件轮询组)。多个EventLoop线程放在一起,可以组成一个EventLoopGroup。反过来说,EventLoopGroup就是一个多线程版本的反应器,其中的单个EventLoop线程对应于一个子反应器(SubReactor)。
NEtty的程序开始不会直接使用单个EventLoop(事件轮询器),而是使用EventLoopGroup。EventLoopGroup的构造函数只有一个参数,用于指定内部的线程数。在构造器初始化时,会按照传如的线程数量在内部构造多个线程和多个EventLoop子反应器(一个线程对应一个EventLoop子反应器),进行多线程的IO事件查询和分发。
如果使用EventLoopGroup的无参构造函数,没有传入线程数量或者传入的数量是0,那么EventLoopGroup内部默认的线程数量为最大可用的CPU处理器是数量的2倍。建设电脑使用的是4核CPU,那么在内部启动8个EventLoop线程,相当于8个子反应器实例。
从前文可以,为了及时接收新连接,在服务端,一般有两个独立的反应器,一个负责新连接的监听和接收,另一个负责IO事件轮询和分发,并且两个反应器相互隔离。对应到NEtty服务器程序中,则需要设置两个EventLoopGroup,一个组负责新连接的监听和接收,另一个组负责IO传输事件的轮询和分发,另个轮询组的职责具体如下:
1、负责新连接的监听和接收的EventLoopGroup中的反应器完成查询通道的新连接IO事件查询。这些反应器有点像负责招工的包工头,因此,该轮询组可以形象地称为“Boss轮询组”。
2、负责IO事件轮询和分发的反应器完成查询所有子通道的IO事件,并且执行对应的Handler处理器完成IO处理——例如数据的输入和输出,这个轮询组可以形象地称为“worker轮询组”。
NEtty的EventLoopGroup与EventLoop之间、EventLoop与Channel之间的关系如下图:

到此介绍完了两个重要的基础概念:父子通道与 EventLoopGroup。接下来正是介绍ServerBootstrap的启动流程。
2 Bootstrap启动流程
Bootstrap的启动流程也就是NEtty组件的组装、配置、以及NEtty服务器或者客户端的启动流程。在本节中对启动流程进行了梳理,大致分为8个步骤。本文仅仅演示的是服务端引导类的使用,用到的引导类为ServerBootstrap。正式使用之前,首选创建一个服务端的引导类实例。
ServerBootstrap b = new ServerBootstrap();
接下来,结合前面的NettyDiscradServer服务器的程序代码,详细介绍一个Bootstrap启动流程中的8个步骤。
第一步:创建反应器轮询组,并设置到ServerBootstrap引导类实例。
public static void test01(){//创建一个服务端的引导类ServerBootstrap b = new ServerBootstrap();//1.创建反应器轮询组,并设置到ServerBootstrap引导类实例//boss轮询组NioEventLoopGroup bossLoopGroups = new NioEventLoopGroup(1);//worker轮询组NioEventLoopGroup workerLoopGroups = new NioEventLoopGroup();//为引导类实例设置反应器轮询组b.group(bossLoopGroups,workerLoopGroups);}
在设置反应器轮询组之前,创建了两个NioEventLoopGroup,一个负责处理连接监听IO事件,称为bossLoopGroups;另一个负责数据传输事件和处理,称为workerLoopGroups。在两个轮询组创建完成后,就可以配置给引导类实例,它一次性地给引导类配置两个轮询组。
如果不需要分开监听新连接事件和输出事件,就不一定非得配置两个轮询组,可以只配置一个EventLoopGroup反应器轮询组。在这种模式下,新连接监听IO事件和数据传输IO事件可能被挤在了同一个线程中处理。这样就会带来一个风险:新连接的接收被更加耗时的数据传输或者业务处理所阻塞。所以在服务端,建议设置两个轮询组的工作模式。
第二步:设置通道的IO类型。Netty不仅支持Java NIO,也支持阻塞式的OIO。下面配置的是Java NIO类型的通道。
//2.设置传输通道的类型为NIO类型
b.channel(NioServerSocketChannel.class);
第三步:设置监听端口。
//3.设置监听端口
b.localAddress(new InetSocketAddress(8080));
第四步:设置传输通道的配置选项。
//4.设置传输通道的参数
b.option(ChannelOption.SO_KEEPALIVE,true);
b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
这里调用了Bootstrap的option()选项设置方法。对于服务器的Bootstrap而言,这个方法的作用是:给父通道设置一些与传输协议相关的选项。如果要给子通道设置一些通道选项,则需要调用childOption()方法。
可以设置那些通道选择呢?在上面的代码中,设置了一个底层TCP相关的选项 ChannelOption.SO_KEEPALIVE。该选项代表是否开始TCP底层心跳机制,true为开启,false为关闭。其他的通道设置选项,下节会介绍。
第五步:装配子通道的Pipeline。每一个通道都用一条ChannelPipeline流水线,它的内部有一个双向链表。装配流水线的方式是:将业务处理器ChannelHandler实例包装之后加入双向链表中。
如何装配Pipeline流水线呢?装配子通道的Handler流水线调用引导类的childHandler方法,该方法需要传入一个ChannelInitializer通道初始化类的示例作为参数。每当父通道成功接收一个连接并创建成功一个子通道后,就会初始化子通道,此时这里配置的ChannelInitializer实例就会被调用。在ChannelInitializer通道初始化类的实例中,有一个initChannel初始化方法,在子通道创建后会被执行,向子通道流水线增加业务处理器。装配子通道的Pipeline流水线的代码如下:
//5.装配子通道流水线b.childHandler(new ChannelInitializer<SocketChannel>() {//有链接到达时,会创建一个通道的子通道,并初始化protected void initChannel(SocketChannel ch){//这里可以管理子通道中的Handler//向子通道流水线添加一个Handler业务处理器ch.pipeline().addLast(new NettyDiscardHandler());}});
为什么仅装配子通道的流水线,而不需要装配父通道的流水线呢?因为父通道的内部业务处理是固定的:接收新连接后,创建子通道,然后初始化子通道,所以不需要特别的配置,由Netty自行进行装配。如果需要完成特殊的父通道业务处理,可以类似地调用ServerBootstrap的handler(ChannelHandler handler)方法,为父通道设置初始化器。
在装配流水线时需要注意:ChannelInitializer处理器有一个泛型参数SocketChannel,这个类型需要和前面的引导类中设置的传输通道类型一一对应。
第六步:开始绑定服务器新连接的监听端口。
//6.开始板顶端口,通过调用sync()同步方法阻塞直到绑定成功
hannelFuture channelFuture = b.bind().sync();
System.out.println("服务器启动成功,监听端口:" + channelFuture.channel().localAddress());
这个也很简单,b.bind()方法的功能是返回一个端口绑定Netty的异步任务channelFuture。这里,并没有channelFuture异步任务增加回调监听器,而是阻塞channelFuture异步任务。直到端口板顶任务执行完成。
在Netty中,所有的IO操作都是异步执行的,这就意味着任何一个IO操作都会立即返回,返回时异步任务还没有真正执行。什么时候执行完成?Netty中的IO操作都会返回异步任务实例(如channelFuture实例)。通过该异步任务实例,既可以实现同步阻塞一直到channelFuture异步任务执行完成,也可以通过为其增加事件监听器的方法注册异步回调逻辑,以获得Netty中的IO操作的真正结果。
第七步:自我阻塞,直到监听通过关闭。
//7.自我阻塞,直到通道关闭安的异步任务结束ChannelFuture closeFuture = channelFuture.channel().closeFuture();closeFuture.sync();
如果要阻塞当前线程直到通道关闭,可以调用通道的closeFuture()方法,已获得通道关闭的异步任务。当通道关闭时,closeFuture实例的sycn方法会返回。
第八步:关闭EvectLoopGroup。
//8 释放所有资源workerLoopGroups.shutdownGracefully();bossLoopGroups.shutdownGracefully();
关闭反应器轮询组,同时也会关闭内部的子反应器线程,也会关闭内部的选择器、内部的轮询线程以及负责查询的所有子通道。在子通道关闭后,会释放底层的资源,如Socket文件描述符等。
3 ChannelOption
无论是对于NioServerSocketChannel父通道类型还是对于NioSocketChannel子通道类型,都可以设置一系列的ChannelOption(通道选项)。ChannelOption类中定义了一些列选项,下面介绍一些常见的选项。
1.SO_RCVBUF和SO_SNDBUF
这两个为TCP传输选项,每个TCP socket(套接字)在内核中都有一个类发送缓冲区和一个接收缓冲区,这两个选项就是用来设置TCP连接的两个缓冲区大小的。TCP的全双工作模式以及TCP的滑动窗口对两个独立的缓冲区都有依赖。
2.TCP_NODELAY
此为TCP传输选项,如果设置为true就表示立即发送数据。TCP_NODELAY用于开启或关闭Nagle算法。如果要求高实时性,有数据发送时就马上发送,就将该选项设置为true(关闭Nagle算法);如果要减少发送次数、减少网路交互,就设置为false(开发Nagle算法),等累计一定大小的数据后再发送。关于TCP_NODELAY的值,Netty模式为true,而操作系统默认为false。
Nagle算法将小的碎片数据连接成更大的报文(或数据包)来最小化所发送报文的数量,如果需要发送一些较小的报文,则需要禁用该算法。
Netty模式禁用Nagle算法,报文会立即发送出去,从而最小化报文传输的延时。
3.SO_KEEPALIVE
此为TCP传输选项,表示是否开启TCP的心跳机制。true为保持连接心跳,默认值为false。启动该功能时,TCP会主动探测空闲连接的有效性。需要注意的是:默认的心跳间隔是7200秒,即2个小时。Netty默认关闭。
4.SO_REUSEADDR
值为true时表示地址复用,默认为false。有四种情况需要用到这个参数设置:
(1)当有一个地址和端口相同的连接socket1处于TIME_WAIT状态时,而又希望启动一个新的连接socket2要占用该地址和端口。
(2)有多块网卡或用IP Alias技术的机器在同一个端口启动多个进程,但每个进程绑定的本地IP地址不能相同。
(3)同一个进程绑定相同的端口到多个socket上,但每个socket绑定的IP地址不同。
(4)完全相同的地址和端口的重复绑定,但这只用于UDP的多播,不用于TCP。
5.SO_LINGER
用来控制socket.close()方法被调用后的行为,包括颜值关闭时间。如果值为 -1,就表示socket.close()方法在调用后立即返回,但操作系统底层会将发送缓冲区的数据全部发送到对端;如果值为0,表示socket.close()方法在调用后会立即返回,但是操作系统会放弃发送缓冲区数据,直接向对端发送RST包,对端将收到复位错误;如果值为非0整数值,就表示调用socket.close()方法的线程被阻塞,直到延迟时间到来,发送缓冲区中的数据发送完毕,若超时,则对端会收到复位错误。模式值为-1,表示禁用该功能。
6.SO_BACKLOG
表示服务端接收连接的队列长度,如果队列已满,客户端连接将被拒绝。服务端在处理客户端新连接请求时(三次握手)是顺序处理的,所以同一时间只能处理一个客户端连接,多个客户端到来的时候,服务端将不能处理的客户端连接请求方法队列中等待处理,队列的大小通过SO_BACKLOG指定。
具体来说,服务端对完成第二次握手的连接放在一个队列(暂称A队列),如果进一步完成第三次握手,再把连接从A队列移动到新队列(B队列),接下来应用程序会通过调用accept()方法取出握手成功后的连接,而系统则会将该连接从B队列中移除。A和B队列的长度之和是SO_BACKLOG指定的值,当A和B队列的长度之和大于SO_BACKLOG值时,新连接将会被TCP内核拒绝。所以,如果SO_BACKLOG过小,accept速度可能会跟不上,A和B队列全满,导致新客户端无法连接。
SO_BACKLOG对程序迟滞的连接数并无影响,影响的只是还没有被accept取出的连接数,也就是三次握手的排队连接数。如果连接建立频繁,服务器处理新连接较慢,可以适当调大这个参数。
相关文章:
Netty核心原理与基础实战(二)——详解Bootstrap
接上篇:Netty核心原理与基础实战(一) 1 Bootstrap基础概念 Bootstrap类是Netty提供的一个便利的工厂类,可以通过它来完成Netty的客户端或服务端的Netty组件的组装,以及Netty程序的初始化和启动执行。Netty的官方解释是…...
C语言常见面试题:C语言中如何进行比较运算?
在C语言中,比较运算用于比较两个值的大小关系。比较运算符包括等于()、不等于(!)、大于(>)、小于(<)、大于等于(>)和小于等于࿰…...
学习总结14
# 【CSGRound1】天下第一 ## 题目背景 天下第一的 cbw 以主席的身份在 8102 年统治全宇宙后,开始了自己休闲的生活,并邀请自己的好友每天都来和他做游戏。由于 cbw 想要显出自己平易近人,所以 zhouwc 虽然是一个蒟蒻,也有能和 c…...
D盘不见了如何恢复?4个恢复方法(新版)!
“很奇怪!我的电脑d盘不知道为什么突然不见了,我还保存了很多重要的文件在里面呢,有什么恢复d盘的方法吗?” 在我们的日常生活中,电脑已经成为了我们工作、学习和娱乐的重要工具。然而,有时候我们会遇到一些…...
vector类的模拟实现
实现基本的vector框架 参考的是STL的一些源码,实现的vector也是看起来像是一个简略版的,但是看完能对vector这个类一些接口函数更好的认识。 我们写写成员变量,先来看看STL的成元变量是那些 namespace tjl {template<class T>class …...
Topaz Photo AI for Mac v2.3.1 补丁版人工智能降噪软件无损放大
想要将模糊的图片变得更加清晰?不妨试试Topaz Photo AI for Mac 这款人工智能、无损放大软件。Topaz Photo AI for Mac 一款强大的人工智能降噪软件,允许用户使用复杂的锐化算法来提高图像清晰度,还包括肖像编辑选项,如面部重塑、…...
【Unity3D小技巧】Unity3D中UI控制解决方案
推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。 一、前言 在开发中总是会控制UI界面,如何优雅的控制UI界面是…...
【状态管理一】概览:状态使用、状态分类、状态具体使用
文章目录 一. 状态使用概览二. 状态的数据类型1. 算子层面2. 接口层面2.1. UML与所有状态类型介绍2.2. 内部状态:InternalKvState 将知识与实际的应用场景、设计背景关联起来,这是学以致用、刨根问底知识的一种直接方式。 本文介绍 状态数据管理&#x…...
SQL--多表查询
我们之前在讲解SQL语句的时候,讲解了DQL语句,也就是数据查询语句,但是之前讲解的查询都是单 表查询,而本章节我们要学习的则是多表查询操作,主要从以下几个方面进行讲解。 多表关系 项目开发中,在进行数据…...
多维时序 | Matlab实现CNN-RVM卷积神经网络结合相关向量机多变量时间序列预测
多维时序 | Matlab实现CNN-RVM卷积神经网络结合相关向量机多变量时间序列预测 目录 多维时序 | Matlab实现CNN-RVM卷积神经网络结合相关向量机多变量时间序列预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 Matlab实现CNN-RVM卷积神经网络结合相关向量机多变量时间序…...
RK3568平台 安卓hal3适配usb camera
一.RK安卓hal3 camera框架 Camera hal3 在 android 框架中所处的位置如上图, 对上,主要实现 Framework 一整套 API 接口,响应其 控制命令,返回数据与控制参数结果。 对下, 主要是通 V4l2 框架实现与 kernel 的交互。3a…...
使用 Visual Studio Code 在远程计算机上调试 PostgreSQL
使用 Visual Studio Code 在远程计算机上调试 PostgreSQL 1. 概述 PostgreSQL 是一个功能强大的开源关系数据库管理系统,适用于各种应用程序。在开发过程中,调试 PostgreSQL 对于识别和解决问题至关重要。在本博客中,我们将手把手教你使用客…...
javascript设计模式之建造者
工厂模式不关心过程,只关心结果,这与建造者相反,建造者更关心的是过程, 这里我们创建一个基类,其拥有技能跟爱好两个属性,还有两个实例方法用来获取技能跟爱好 // 基类 let Human function (param {}) …...
安擎科技携手华为云区块链共同打造安全天空
当前,低空经济崛起,无人机多并发、混合运行时引发的网络信息安全、空域安全问题已成行业首要课题。 在2024年1月正式实施的《民用无人驾驶航空器运行安全管理规则》(CCAR-92)第549条中规定,“无人驾驶航空器航行服务提…...
学习数据结构的第一天
结构体 如何定义结构体 1、先定义结构体类型,再定义结构体类型变量 struct student/定义学生结构体类型/ { long number; char name[20]; char sex; int age; float score[3];/三科考试成绩/ }2、定义结构体类型同时定义结构体类型变量 struct student/定义学生结…...
5.electron之主进程起一个本地服务
如果可以实现记得点赞分享,谢谢老铁~ Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 Electron 将 Chromium 和 Node.js 嵌入到了一个二进制文件中,因此它允许你仅需一个代码仓库,就可以撰写支持 Windows、…...
爬取58二手房并用SVR模型拟合
目录 一、前言 二、爬虫与数据处理 三、模型 一、前言 爬取数据仅用于练习和学习。本文运用二手房规格sepc(如3室2厅1卫)和二手房面积area预测二手房价格price,只是练习和学习,不代表任何实际意义。 二、爬虫与数据处理 import requests import cha…...
鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之RichText组件
鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之RichText组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、RichText组件 鸿蒙(HarmonyOS)富文本组件,…...
7.electron之渲染线程发送事件,主进程监听事件
如果可以实现记得点赞分享,谢谢老铁~ Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 Electron 将 Chromium 和 Node.js 嵌入到了一个二进制文件中,因此它允许你仅需一个代码仓库,就可以撰写支持 Windows、…...
thinkphp6入门(19)-- 中间件向控制器传参
可以通过给请求对象赋值的方式传参给控制器(或者其它地方),例如 <?phpnamespace app\middleware;class Hello {public function handle($request, \Closure $next){$request->hello ThinkPHP;return $next($request);} } 然后在控制…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...
Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...
基于Springboot+Vue的办公管理系统
角色: 管理员、员工 技术: 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能: 该办公管理系统是一个综合性的企业内部管理平台,旨在提升企业运营效率和员工管理水…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...
微服务通信安全:深入解析mTLS的原理与实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、引言:微服务时代的通信安全挑战 随着云原生和微服务架构的普及,服务间的通信安全成为系统设计的核心议题。传统的单体架构中&…...
深度解析:etcd 在 Milvus 向量数据库中的关键作用
目录 🚀 深度解析:etcd 在 Milvus 向量数据库中的关键作用 💡 什么是 etcd? 🧠 Milvus 架构简介 📦 etcd 在 Milvus 中的核心作用 🔧 实际工作流程示意 ⚠️ 如果 etcd 出现问题会怎样&am…...
