12.Netty源码之整体架构脉络

news2024/11/18 8:15:00

Netty 整体架构脉络

Netty 的逻辑处理架构为典型网络分层架构设计,共分为网络通信层、事件调度层、服务编排层,每一层各司其职。

image.png

网络通信层

网络通信层的职责是执行网络 I/O 的操作。它支持多种网络协议和 I/O 模型的连接操作。当网络数据读取到内核缓冲区后,会触发各种网络事件,这些网络事件会分发给事件调度层进行处理。

网络通信层的核心组件包含BootStrap、ServerBootStrap、Channel三个组件

BootStrap

Bootstrap 是“引导”的意思,它主要负责整个 Netty 程序的启动、初始化、服务器连接等过程,它相当于一条主线,串联了 Netty 的其他核心组件。

Netty 中的引导器共分为两种类型:

一个为用于客户端引导的 Bootstrap。

另一个为用于服务端引导的 ServerBootStrap,它们都继承自抽象类 AbstractBootstrap。

Bootstrap 和 ServerBootStrap 十分相似,两者非常重要的区别在于:

客户端 Bootstrap 可用于连接远端服务器,只绑定一个 EventLoopGroup。

而 ServerBootStrap 则用于服务端启动绑定本地端口,会绑定两个 EventLoopGroup,这两个 EventLoopGroup 通常称为 Boss 和 Worker。

为什么客户端的BootStrap要绑定一个EventLoopGroup呢?

因为服务器端发送消息给客户端,客户端也是需要一个EventLoopGroup处理。

ServerBootStrap 中的 Boss 和 Worker 是什么角色呢?它们之间又是什么关系?

这里的 Boss 和 Worker 可以理解为“老板”和“员工”的关系。

每个服务器中都会有一个 Boss,也会有一群做事情的 Worker。

Boss 会不停地接收新的连接,然后将连接分配给一个个 Worker 处理连接。

Channel

比如ServerSocketChannel NioSocketChannel。

Channel 的字面意思是“通道”,它是网络通信的载体。

Channel提供了基本的 API 用于网络 I/O 操作,

如 register:注册到eventloop、bind:绑定端口、connect:连接服务器端、read、write、flush 等。

Netty 自己实现的 Channel 是以 JDK的NIOChannel 为基础的,相比较于 JDK NIO,Netty 的 Channel 提供了更高层次的抽象,同时屏蔽了底层 Socket 的复杂性,赋予了 Channel 更加强大的功能,你在使用 Netty 时基本不需要再与 Java Socket 类直接打交道。

下图是 Channel 家族的图谱。AbstractChannel 是整个家族的基类,派生出 AbstractNioChannel、

AbstractOioChannel、AbstractEpollChannel 等子类,每一种都代表了不同的 I/O 模型和协议类型。

常用的 Channel 实现类有:

Drawing 3.png

NioServerSocketChannel 异步TCP服务端。可以监听接入TCP连接的通道。 ​ NioSocketChannel 异步TCP客户端。客户端跟服务端进行通信的。是连接到TCP网络socket(套接字)的通道 ​ OioServerSocketChannel 同步 TCP 服务端。 ​ OioSocketChannel 同步 TCP 客户端。 ​ NioDatagramChannel 异步 UDP 连接。 ​ OioDatagramChannel 同步 UDP 连接。

当然 Channel 会有多种状态,如连接建立、连接注册、数据读写、连接销毁等。随着状态的变化,Channel 处于不同的生命周期,每一种状态都会绑定相应的事件回调,下面的表格我列举了 Channel 最常见的状态所对应的事件回调。

事件 说明 channelRegistered Channel 创建后被注册到EventLoop上 channelUnregistered Channel 创建后从EventLoop取消注册 channelActive Channel 处于就绪状态,可以被读写 channelInactive Channel 处于非就绪状态 channelRead Channel 可以从远端读取到数据 channelReadComplete Channel 读取数据完成

BootStrap 和 ServerBootStrap 分别负责客户端和服务端的启动,它们是非常强大的辅助工具类;

Channel 是网络通信的载体,提供了与底层 Socket 交互的能力。

那么 Channel 生命周期内的事件都是如何被处理的呢?

那就是 Netty 事件调度层的工作职责了。

事件调度层

事件调度层的职责是通过 Reactor 线程模型对各类事件进行聚合处理,通过 Selector 主循环线程集成多种事件( I/O

事件、信号事件、定时事件等),实际的业务处理逻辑是交由服务编排层中相关的 Handler 完成。

事件调度层的核心组件包括 EventLoopGroup、EventLoop。

EventLoopGroup

EventLoopGroup 本质是一个线程池,主要负责接收 I/O 请求,并分配线程执行处理请求。

在下图中,我为你讲述了 EventLoopGroups、EventLoop 与 Channel 的关系。

Drawing 4.png

EventLoopGroup、EventLoop、Channel的关系

从上图中,我们可以总结出 EventLoopGroup、EventLoop、Channel 的几点关系。

EventLoop和线程绑定

一个 EventLoopGroup 往往包含一个或者多个 EventLoop。EventLoop 同一时间会与一个线程绑定,每个EventLoop负责处理多个 Channel

1个EventLoop可以理解为1个线程 ,EventLoop 用于处理 Channel 生命周期内的所有 I/O 事件。

如 accept、connect、read、write 等 I/O 事件。

1个EventLoop处理N个Channel

1个channel绑定1个EventLoop

每新建一个 Channel,EventLoopGroup 会选择一个 EventLoop 与其绑定。

该 Channel 在生命周期内都可以对 EventLoop 进行多次绑定和解绑。

下图是 EventLoopGroup 的家族图谱。可以看出 Netty 提供了 EventLoopGroup 的多种实现,而且 EventLoop 则是 EventLoopGroup 的子接口,所以也可以把 EventLoop 理解为 EventLoopGroup,但是它只包含一个 EventLoop 。

Drawing 5.png

EventLoop的实现类是 NioEventLoop。

EventLoopGroup 的实现类是 NioEventLoopGroup。

NioEventLoopGroup 也是 Netty 中最被推荐使用的线程模型。

NioEventLoopGroup 继承于 MultiThreadEventLoopGroup,是基于 NIO 模型开发的,可以把 NioEventLoopGroup 理解为一个线程池,NioEventLoop理解为1个线程。

每个线程负责处理多个 Channel,而同一个 Channel 只会对应一个线程。

EventLoopGroup 是 Netty 的核心处理引擎,那么 EventLoopGroup和之前所提到的 Reactor 线程模型到底是什么关系呢?

其实 EventLoopGroup 是 Netty Reactor 线程模型的具体实现方式,Netty 通过创建不同的 EventLoopGroup 参数配置,就可以支持 Reactor 的三种线程模型:

单线程模型:EventLoopGroup 只包含一个 EventLoop,Boss 和 Worker 使用同一个EventLoopGroup;

多线程模型:EventLoopGroup 包含多个 EventLoop,Boss 和 Worker 使用同一个EventLoopGroup;

主从多线程模型:EventLoopGroup 包含多个 EventLoop,Boss 是主 Reactor,Worker 是从 Reactor,它们分别使用不同的 EventLoopGroup,主 Reactor 负责接受新的网络连接 Channel 创建,然后把 Channel 注册到从 Reactor。从 Reactor负责监听读写事件。

服务编排层

服务编排层的职责是负责组装各类服务,它是 Netty 的核心处理链,用以实现网络事件的动态编排和有序传播。

服务编排层的核心组件包括 ChannelPipeline、ChannelHandler、ChannelHandlerContext。

handler方法和childHandler方法

java 在基类AbstractBootstrap有handler方法,目的是添加一个handler,监听Bootstrap的动作,客户端的Bootstrap中,继承了这一点。 ​ 在服务端的ServerBootstrap中增加了一个方法childHandler,它的目的是添加handler,用来监听已经连接的客户端的Channel的动作和状态。 ​ handler()和childHandler()的主要区别是,handler()是发生在初始化的时候。初始化指的是accept接收到请求,对客户端socketChannel进行初始化。 ​ childHandler()是发生在客户端连接之后。 ​ 也就是说,如果需要在客户端连接前的请求进行handler处理,则需要配置在handler()方法中处理,如果是处理客户端连接之后的handler,则需要配置在childHandler()。 ​ 通过handler添加的handlers是对bossGroup线程组起作用,通过childHandler添加的handlers是对workerGroup线程组起作用,客户端Bootstrap只有handler()方法,因为客户端只需要一个事件线程组( ​ b.childHandler(new ChannelInitializer<SocketChannel>() {                 @Override                 public void initChannel(SocketChannel ch) throws Exception {                     ChannelPipeline p = ch.pipeline();                     p.addLast(new LoggingHandler(LogLevel.INFO));                     p.addLast(serverHandler);                 }             }); ​ pipeline是伴随Channel的存在而存在的,交互信息通过它进行传递,我们可以addLast(或者addFirst)多个handler,第一个参数是名字,无具体要求,如果填写null,系统会自动命名。

ChannelPipeline

ChannelPipeline 是 Netty 的核心编排组件,负责组装各种 ChannelHandler,实际数据的编解码以及加工处理操作都是由 ChannelHandler 完成的。

ChannelPipeline 可以理解为ChannelHandler 的实例列表——内部通过双向链表将不同的 ChannelHandler 链接在一起。当 I/O 读写事件触发时,ChannelPipeline 会依次调用 ChannelHandler 列表对 Channel 的数据进行拦截和处理。

ChannelPipeline 是线程安全的,因为每一个新的 Channel 都会对应绑定一个新的 ChannelPipeline。

一个 ChannelPipeline 关联一个 EventLoop,一个 EventLoop 仅会绑定一个线程。

ChannelPipeline、ChannelHandler 都是高度可定制的组件。

开发者可以通过这两个核心组件掌握对 Channel 数据操作的控制权。下面我们看一下 ChannelPipeline 的结构图:

image.png

从上图可以看出,ChannelPipeline 中包含入站 ChannelInboundHandler 和出站 ChannelOutboundHandler 两种处理器,我们结合客户端和服务端的数据收发流程来理解 Netty 的这两个概念。

Drawing 7.png

客户端和服务端都有各自的 ChannelPipeline。

以客户端为例,数据从客户端发向服务端,该过程称为出站,反之则称为入站。

数据入站会由一系列 服务器端的InBoundHandler 处理,然后再以相反方向的 服务器端的OutBoundHandler 处理后完成出站。

我们经常使用的编码 Encoder 是出站操作,解码 Decoder 是入站操作。

服务端接收到客户端数据后,需要先经过 Decoder 入站处理后,再通过 Encoder 出站通知客户端。

所以客户端和服务端一次完整的请求应答过程可以分为4个步骤:

客户端出站(编码请求数据)、

服务端入站(解码数据并执行业务逻辑)、

服务端出站(编码响应结果)

客户端入站(解码响应数据,执行业务逻辑)。

ChannelHandler&ChannelHandlerContext

数据的编解码工作以及其他转换工作实际都是通过 ChannelHandler 处理的。

站在开发者的角度,最需要关注的就是 ChannelHandler,我们很少会直接操作 Channel,都是通过 ChannelHandler 间接完成。

下图描述了 Channel 与 ChannelPipeline 的关系,从图中可以看出,每创建一个 Channel 都会绑定一个新的 ChannelPipeline,ChannelPipeline 中每加入一个 ChannelHandler ,ChannelHandler都会绑定一个 ChannelHandlerContext。

ChannelPipeline、ChannelHandlerContext、ChannelHandler 三个组件的关系是密切相关的,那么你一定会有疑问,每个 ChannelHandler 绑定ChannelHandlerContext 的作用是什么呢?

Drawing 8.png

ChannelHandlerContext 用于保存 ChannelHandler 上下文,通过 ChannelHandlerContext 我们可以知道 ChannelPipeline 和 ChannelHandler 的关联关系。

ChannelHandlerContext 可以实现 ChannelHandler 之间的交互,ChannelHandlerContext 包含了

ChannelHandler 生命周期的所有事件,如 connect、bind、read、flush、write、close 等。

ChannelHandler 中又持有ChannelPipeline 。

此外,你可以试想这样一个场景。

如果每个 ChannelHandler 都有一些通用的逻辑需要实现,没有 ChannelHandlerContext 这层模型抽象,你是不是需要写很多相同的代码呢?

每个新建的 Channel 会绑定一个新的ChannelPipeline,每一个 ChannelPipeline 都包含多个 ChannelHandlerContext,所有 ChannelHandlerContext 之间组成了双向链表。

又因为每个 ChannelHandler 都对应一个 ChannelHandlerContext,所以实际上 ChannelPipeline 维护的是它与 ChannelHandlerContext 的关系。

以上便是 Netty 的逻辑处理架构,可以看出 Netty 的架构分层设计得非常合理,屏蔽了底层 NIO 以及框架层的实现细节,对于业务开发者来说,只需要关注业务逻辑的编排和实现即可。

组件关系梳理

当你了解每个 Netty 核心组件的概念后。你会好奇这些组件之间如何协作?结合客户端和服务端的交互流程,我画了一张图,为你完整地梳理一遍 Netty 内部逻辑的流转。

Drawing 9.png

Netty整体流程

java 服务端 启动初始化时有 Boss EventLoopGroup 和 Worker EventLoopGroup 两个组件,其中 Boss-EventLoopGroup负责监听网络连接事件。当有新的网络连接事件到达时,则创建Channel并将 Channel 注册到 Worker-EventLoopGroup。 ​ Worker-EventLoopGroup 会被分配一个 EventLoop 负责处理该 Channel 的读写事件。每个 EventLoop 都是单线程的,通过 Selector 进行事件循环。每一个 nioeventloop 的 selector 对应的是同一个对象,这两个对象是一一对应的。 ​ 当客户端发起 I/O 读写事件时,会通过selector绑定EventLoop , 服务端 EventLoop 会进行数据的读取,然后通过 Pipeline 触发各种监听器进行数据的加工处理。 ​ 客户端数据会被传递到 ChannelPipeline 的第一个 ChannelInboundHandler 中,数据处理完成后,将加工完成的数据传递给下一个 ChannelInboundHandler。 ​ 当数据写回客户端时,会将处理结果在 ChannelPipeline 的 ChannelOutboundHandler 中传播,最后到达客户端。

以上便是 Netty 各个组件的整体交互流程,你只需要对每个组件的工作职责有所了解,心中可以串成一条流水线即可,具体每个组件的实现原理后续我们会深入介绍。

视频学习链接:https://www.bilibili.com/video/BV19g4y1H7iy?p=3&vd_source=2fe0aba0a7e0c99e9d0d910556f2b12f

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/789003.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一分钟叫你怎样AI绘画 Vega Ai

先看效果图&#xff1a; 是不是也想自己去创造这样的图片呢&#xff0c;注意已经不需要自己画了&#xff01;&#xff01; Vega AI 简介 Vega AI是一款能够 文字生成图片、根据图片文字进行生成图片、条件生成图片 、根据多张图片训练出自己的风格&#xff0c;在风格广场选择…

使用 OpenCV 和 GrabCut 算法进行交互式背景去除

一、说明 我想&#xff0c;任何人都可以尝试从图像中删除背景。当然&#xff0c;有大量可用的软件或工具能够做到这一点&#xff0c;但其中一些可能很昂贵。但是&#xff0c;我知道有人使用窗口绘画3D魔术选择或PowerPoint背景去除来删除背景。 如果您是计算机视觉领域的初学者…

Linux系统知识1—Linux命令基础格式,什么是命令,命令行,ls命令入门,ls命令的参数和选项,-a,-l -h选项的使用及组合使用

一.什么是命令&#xff0c;命令行 &#xff0e;命令行&#xff1a;即 Linux 终端&#xff08; Terminal )&#xff0c;是一种命令提示符页面。以纯"字符"的形式操作系统&#xff0c;可以使用各种字符化命令对系统发出操作指令。 &#xff0e;命令&#xff1a;即 Lin…

【LangChain】检索器之上下文压缩

LangChain学习文档 【LangChain】检索器(Retrievers)【LangChain】检索器之MultiQueryRetriever【LangChain】检索器之上下文压缩 上下文压缩 LangChain学习文档 概要内容使用普通向量存储检索器使用 LLMChainExtractor 添加上下文压缩(Adding contextual compression with an…

数据结构基本概念及算法分析

文章目录 1. 数据结构基本概念1.1 基本概念和术语1.1.1 数据1.1.2 数据元素1.1.3 数据项1.1.4 数据对象1.1.5 数据结构 1.2 逻辑结构与物理结构1.2.1 逻辑结构(我们最需要关注的问题)1.2.2 物理机构 1.3 数据类型1.3.1 数据类型定义1.3.2 抽象数据类型 2. 算法分析2.1 算法的复…

【Python机器学习】实验02 线性回归

文章目录 线性回归1. 单变量的线性回归1.1 数据读取1.2 训练数据的准备1.3 假设函数定义--假设函数是为了去预测1.4 损失函数的定义1.5 利用梯度下降算法来优化参数w1.6 可视化误差曲线1.7 可视化回归线/回归平面 1.2 单变量的线性回归--基于sklearn试试&#xff1f;1.3 多变量…

object tracking论文代码汇总

文章目录 2023Segment and Track AnythingTrack Anything: Segment Anything Meets VideosSAM-DA: UAV Tracks Anything at Night with SAM-Powered Domain Adaptation 2023 Segment and Track Anything code&#xff1a;https://github.com/z-x-yang/Segment-and-Track-Anyt…

响应式赋值Object.assign()和JSON.parse(JSON.stringify())的区别

一、需求&#xff1a;点击编辑弹出编辑框&#xff0c;修改后的内容点击认按钮修改后的数据更新回显到原列表。今天优化代码的时候发现了Object.assign()和JSON.parse(JSON.stringify())的区别。 优化前代码如下&#xff1a; // 编辑药品回显editMedicData(data) {this.table…

会员系统怎么搭建,适合门店的会员系统有哪些?

会员系统是一种为企业和门店提供会员管理和服务的工具。会员系统可以通过提供专属优惠、积分奖励、个性化推荐等方式&#xff0c;激励顾客成为会员并保持长期关系。 我们在自己搭建或选择会员系统时&#xff0c;需要考虑门店的特定需求以及系统的功能、可靠性、易用性和成本等因…

github前端开源json2html

软件介绍 前端低代码工具包&#xff0c;通过 JSON 配置就能生成各种页面。 应用场景 json解析超大数据动态渲染&#xff0c;渲染速度、性能解决问题 包引用列表 vue3 (cdn模式开发)element plusnodehttp-serveraxios 操作步骤 1.环境准备下载node&#xff1a;https://no…

长tree用buffer还是inverter?驱动强度如何选型?

相关文章链接: 静态时序分析: 最小脉冲宽度检查 redhawk:clock buffer cluster 面试中关于CTS buf/inv选型的问题经久不衰,依托经验,不看纸面信息,inverter和buffer各有优劣,同驱动buffer实际推力更强,意味着只用buffer,clock repeater数量更少,inverter必须成对的…

从零开始搭建医药领域知识图谱实现智能问答与分析服务(含码源):含Neo4j基于垂直网站数据的医药知识图谱构建、医药知识图谱的自动问答等

项目设计集合&#xff08;人工智能方向&#xff09;&#xff1a;助力新人快速实战掌握技能、自主完成项目设计升级&#xff0c;提升自身的硬实力&#xff08;不仅限NLP、知识图谱、计算机视觉等领域&#xff09;&#xff1a;汇总有意义的项目设计集合&#xff0c;助力新人快速实…

抖音seo矩阵系统源码保姆式开发部署指导

抖音seo霸屏&#xff0c;是一种专为抖音视频创作者和传播者打造的视频批量剪辑&#xff0c;批量分发产品。使用抖音seo霸屏软件&#xff0c;可以帮助用户快速高效的制作出高质量的优质视频。 使用方法&#xff1a;1. 了解用户的行为习惯 2. 充分利用自身资源进行开发 3. 不…

超全整理,selenium自动化测试常见问题解决(汇总)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 自动化代码中&…

静态路由小实验

文章目录 一、实验要求及拓扑图二、实验步骤三、思考题 一、实验要求及拓扑图 二、实验步骤 1、创建VLAN&#xff0c;将端口划入vlan 在交换机S3、S4上创建VLAN10、20 Switch(config)#vl 10 Switch(config-vlan)#vl 20 S3(config)#int f0/3 S3(config-if)#switchport access …

SpringBoot中使用测试框架MockMvc来模拟HTTP请求测试Controller接口

场景 Java中进行单元测试junit.Assert断言、Mockito模拟对象、verify验证模拟结果、Java8中lambda的peek方法使用&#xff1a; Java中进行单元测试junit.Assert断言、Mockito模拟对象、verify验证模拟结果、Java8中lambda的peek方法使用_assert java8_霸道流氓气质的博客-CSD…

17的勒索软件攻击泄露关键OT信息

数据泄漏一直是企业关注的问题&#xff0c;敏感信息泄露可能导致声誉受损、法律处罚、知识产权损失、甚至影响员工和客户的隐私。然而很少有关于工业企业面临的威胁行为者披露其OT安全、生产、运营或技术的敏感细节的研究。 2021年&#xff0c;Mandiant威胁情报研究发现&#…

ambari管理配置组实现针对不同节点使用不同配置

实操 一.新建配置组&#xff1a; 二.取名后指定该配置组针对哪些节点生效&#xff1a; 三.添加节点&#xff1a; 保存后有个空的配置组newMR2. 四.接下来在该配置组内自定义一些配置参数&#xff0c;比如单独针对节点hdp01配置fetch最高并发度为20&#xff1a; 五.重…

区块链服务网络的顶层设计与应用实践

日前&#xff0c;2023全球数字经济大会专题论坛&#xff1a;Web3.0发展趋势专题论坛暨2023区块链、元宇宙蓝皮书发布会在北京举行。本次论坛上隆重发布了《中国区块链发展报告&#xff08;2023&#xff09;》&#xff0c;对我国区块链行业在2022年的发展状况进行了总结梳理&…

【英飞凌PSoC 6】使用软件和硬件I2C点亮OLED屏,帧率从2FPS提升到51FPS

文章目录 一、准备工作1.1 硬件准备1.2 软件准备1.3 硬件连接 二、原理分析2.1 开发板原理图2.2 芯片数据手册 三、软件I2C驱动OLED3.1 创建RT-Thread项目3.2 添加ssd1306软件包3.3 配置软件I2C和ssd1306软件包3.4 编译和下载程序3.5 运行和测试程序 四、硬件I2C驱动OLED4.1 增…