Netty工作原理最详细分析

2023-10-26

  • NIO通讯服务端步骤:

1、创建ServerSocketChannel,为它配置非阻塞模式
2、绑定监听,配置TCP参数,录入backlog大小等
3、创建一个独立的IO线程,用于轮询多路复用器Selector
4、创建Selector,将之前的ServerSocketChannel注册到Selector上,并设置监听标识位SelectionKey.ACCEPT
5、启动IO线程,在循环体中执行Selector.select()方法,轮询就绪的通道
6、当轮询到处于就绪的通道时,需要进行判断操作位,如果是ACCEPT状态,说明是新的客户端介入,则调用accept方法接受新的客户端。
7、设置新接入客户端的一些参数,并将其通道继续注册到Selector之中。设置监听标识等
8、如果轮询的通道操作位是READ,则进行读取,构造Buffer对象等
9、更细节的还有数据没发送完成继续发送的问题

  • 零拷贝技术原理

  • “零拷贝”是指计算机操作的过程中,CPU不需要为数据在内存之间的拷贝消耗资源。而它通常是指计算机在网络上发送文件时,不需要将文件内容拷贝到用户空间(User Space)而直接在内核空间(Kernel Space)中传输到网络的方式
  • Netty的“零拷贝”主要体现在三个方面
    • Netty的接收和发送ByteBuffer采用DIRECT BUFFERS,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAP BUFFERS)进行Socket读写,JVM会将堆内存Buffer拷贝一份到直接内存中,然后才写入Socket中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝读取直接从“堆外直接内存”,不像传统的堆内存和直接内存拷贝,ByteBufAllocator 通过ioBuffer分配堆外内存
    • Netty提供了组合Buffer对象,可以聚合多个ByteBuffer对象,用户可以像操作一个Buffer那样方便的对组合Buffer进行操作,避免了传统通过内存拷贝的方式将几个小Buffer合并成一个大的Buffer,Netty允许我们将多段数据合并为一整段虚拟数据供用户使用,而过程中不需要对数据进行拷贝操作,组合Buffer对象,避免了内存拷贝,ChannelBuffer接口:Netty为需要传输的数据制定了统一的ChannelBuffer接口,使用getByte(int index)方法来实现随机访问,使用双指针的方式实现顺序访问
    •  Netty主要实现了HeapChannelBuffer,ByteBufferBackedChannelBuffer,与Zero Copy直接相关的CompositeChannelBuffer类
      • CompositeChannelBuffer
        • CompositeChannelBuffer类的作用是将多个ChannelBuffer组成一个虚拟的ChannelBuffer来进行操作
        • 为什么说是虚拟的呢,因为CompositeChannelBuffer并没有将多个ChannelBuffer真正的组合起来,而只是保存了他们的引用,这样就避免了数据的拷贝,实现了Zero Copy,内部实现
          • 其中readerIndex既读指针和writerIndex既写指针是从AbstractChannelBuffer继承而来的
          • components是一个ChannelBuffer的数组,他保存了组成这个虚拟Buffer的所有子Buffer
          • indices是一个int类型的数组,它保存的是各个Buffer的索引值
          • lastAccessedComponentId是一个int值,它记录了最后一次访问时的子Buffer ID
        • CompositeChannelBuffer实际上就是将一系列的Buffer通过数组保存起来,然后实现了ChannelBuffer 的接口,使得在上层看来,操作这些Buffer就像是操作一个单独的Buffer一样
    • Netty的文件传输采用了transferTo方法,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write方式导致的内存拷贝问题
      • Linux中的sendfile()以及Java NIO中的FileChannel.transferTo()方法都实现了零拷贝的功能,而在Netty中也通过在FileRegion中包装了NIO的FileChannel.transferTo()方法实现了零拷贝

Netty 的 Zero-copy 体现在如下几个个方面:

  1. Netty 提供了 CompositeByteBuf 类, 它可以将多个 ByteBuf 合并为一个逻辑上的 ByteBuf, 避免了各个 ByteBuf 之间的拷贝。
  2. 通过 wrap 操作, 我们可以将byte[] 数组、ByteBuf、ByteBuffer等包装成一个 Netty ByteBuf 对象, 进而避免了拷贝操作。
  3. ByteBuf 支持 slice 操作,因此可以将 ByteBuf 分解为多个共享同一个存储区域的ByteBuf, 避免了内存的拷贝。
  4. 通过 FileRegion 包装的FileChannel.tranferTo 实现文件传输, 可以直接将文件缓冲区的数据发送到目标 Channel, 避免了传统通过循环 write 方式导致的内存拷贝问题。
  • 技术栈

  • Netty的三层网络架构进行设计

https://images2015.cnblogs.com/blog/273403/201706/273403-20170608193637043-1571391636.png

  1. 第一层:Reactor通信调度层。该层的主要职责就是监听网络的连接和读写操作,负责将网络层的数据读取到内存缓冲区中,然后触发各种网络事件,例如连接创建、连接激活、读事件、写事件等,将这些事件触发到Pipeline中,再由Pipeline充当的职责链来进行后续的处理。
  2. 第二层:职责链Pipeline层。负责事件在职责链中有序的向前(后)传播,同时负责动态的编排职责链。Pipeline可以选择监听和处理自己关心的事件。
  3. 第三层:业务逻辑处理层,一般可分为两类:a. 纯粹的业务逻辑处理,例如日志、订单处理。b. 应用层协议管理,例如HTTP(S)协议、FTP协议等。

影响网络服务通信性能的主要因素有:网络I/O模型、线程(进程)调度模型和数据序列化方式。

在网络I/O模型方面,Netty采用基于非阻塞I/O的实现,底层依赖的是JDKNIO框架的Selector。

在线程调度模型方面,Netty采用Reactor线程模型。常用的Reactor线程模型有三种,分别是:

a、Reactor单线程模型:Reactor单线程模型,指的是所有的I/O操作都在同一个NIO线程上面完成。对于一些小容量应用场景,可以使用单线程模型。

b、Reactor多线程模型:Rector多线程模型与单线程模型最大的区别就是有一组NIO线程处理I/O操作。主要用于高并发、大业务量场景。

c、主从Reactor多线程模型:主从Reactor线程模型的特点是服务端用于接收客户端连接的不再是一个单独的NIO线程,而是一个独立的NIO线程池。利用主从NIO线程模型,可以解决一个服务端监听线程无法有效处理所有客户端连接的性能不足问题。Netty线程模型并非固定不变的,它可以支持三种Reactor线程模型。

在数据序列化方面,影响序列化性能的主要因素有:

a、序列化后的码流大小(网络带宽占用)。

b、序列化和反序列化操作的性能(CPU资源占用)。

c、并发调用时的性能表现:稳定性、线性增长等。

Netty默认提供了对GoogleProtobuf二进制序列化框架的支持,但通过扩展Netty的编解码接口,可以实现其它的高性能序列化框架,例如Avro、Thrift的压缩二进制编解码框架。

 

  • 主从Reactor多线程模型

Netty使用ChannelBuffer来存储并操作读写的网络数据。ChannelBuffer除了提供和ByteBuffer类似的方法,还提供了 一些实用方法,具体可参考其API文档。

ChannelBuffer的实现类有多个,这里列举其中主要的几个:

1)HeapChannelBuffer:这是Netty读网络数据时默认使用的ChannelBuffer,这里的Heap就是Java堆的意思,因为 读SocketChannel的数据是要经过ByteBuffer的,而ByteBuffer实际操作的就是个byte数组,所以 ChannelBuffer的内部就包含了一个byte数组,使得ByteBuffer和ChannelBuffer之间的转换是零拷贝方式。根据网络字 节续的不同,HeapChannelBuffer又分为BigEndianHeapChannelBuffer和 LittleEndianHeapChannelBuffer,默认使用的是BigEndianHeapChannelBuffer。Netty在读网络 数据时使用的就是HeapChannelBuffer,HeapChannelBuffer是个大小固定的buffer,为了不至于分配的Buffer的 大小不太合适,Netty在分配Buffer时会参考上次请求需要的大小。

2)DynamicChannelBuffer:相比于HeapChannelBuffer,DynamicChannelBuffer可动态自适应大 小。对于在DecodeHandler中的写数据操作,在数据大小未知的情况下,通常使用DynamicChannelBuffer。

3)ByteBufferBackedChannelBuffer:这是directBuffer,直接封装了ByteBuffer的 directBuffer。

对于读写网络数据的buffer,分配策略有两种:1)通常出于简单考虑,直接分配固定大小的buffer,缺点是,对一些应用来说这个大小限制有时是不 合理的,并且如果buffer的上限很大也会有内存上的浪费。2)针对固定大小的buffer缺点,就引入动态buffer,动态buffer之于固定 buffer相当于List之于Array。

buffer的寄存策略常见的也有两种(其实是我知道的就限于此):1)在多线程(线程池) 模型下,每个线程维护自己的读写buffer,每次处理新的请求前清空buffer(或者在处理结束后清空),该请求的读写操作都需要在该线程中完成。 2)buffer和socket绑定而与线程无关。两种方法的目的都是为了重用buffer。

Netty对buffer的处理策略是:读 请求数据时,Netty首先读数据到新创建的固定大小的HeapChannelBuffer中,当HeapChannelBuffer满或者没有数据可读 时,调用handler来处理数据,这通常首先触发的是用户自定义的DecodeHandler,因为handler对象是和ChannelSocket 绑定的,所以在DecodeHandler里可以设置ChannelBuffer成员,当解析数据包发现数据不完整时就终止此次处理流程,等下次读事件触 发时接着上次的数据继续解析。就这个过程来说,和ChannelSocket绑定的DecodeHandler中的Buffer通常是动态的可重用 Buffer(DynamicChannelBuffer),而在NioWorker中读ChannelSocket中的数据的buffer是临时分配的 固定大小的HeapChannelBuffer,这个转换过程是有个字节拷贝行为的。

从该结构图也可以看到,Channel主要提供的功能如下:

1)当前Channel的状态信息,比如是打开还是关闭等。 2)通过ChannelConfig可以得到的Channel配置信息。 3)Channel所支持的如read、write、bind、connect等IO操作。 4)得到处理该Channel的ChannelPipeline,既而可以调用其做和请求相关的IO操作。

在Channel实现方面,以通常使用的nio socket来说,Netty中的NioServerSocketChannel和NioSocketChannel分别封装了java.nio中包含的 ServerSocketChannel和SocketChannel的功能。

如前所述,Netty是事件驱动的,其通过ChannelEvent来确定事件流的方向。一个ChannelEvent是依附于Channel的 ChannelPipeline来处理,并由ChannelPipeline调用ChannelHandler来做具体的处理。下面是和 ChannelEvent相关的接口及类图:

http://incdn1.b0.upaiyun.com/2015/04/cd7fd1517e323f26c6f1b0b6b96e3b3d.png

对于使用者来说,在ChannelHandler实现类中会使用继承于ChannelEvent的MessageEvent,调用其 getMessage()方法来获得读到的ChannelBuffer或被转化的对象。

Netty 在事件处理上,是通过ChannelPipeline来控制事件流,通过调用注册其上的一系列ChannelHandler来处理事件,这也是典型的拦截 器模式。下面是和ChannelPipeline相关的接口及类图:

http://incdn1.b0.upaiyun.com/2015/04/815e6212def15fe76ed27cec7a393d59.png

事件流有两种,upstream事件和downstream事件。在ChannelPipeline中,其可被注册的ChannelHandler既可以 是 ChannelUpstreamHandler 也可以是ChannelDownstreamHandler ,但事件在ChannelPipeline传递过程中只会调用匹配流的ChannelHandler。在事件流的过滤器链 中,ChannelUpstreamHandler或ChannelDownstreamHandler既可以终止流程,也可以通过调用 ChannelHandlerContext.sendUpstream(ChannelEvent)或 ChannelHandlerContext.sendDownstream(ChannelEvent)将事件传递下去。

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Netty工作原理最详细分析 的相关文章

  • 时代选出最重要 AI 100人,多位华人上榜

    来源 Time 链接 https time com collection time100 ai 编译 芯芯 编辑 靖宇 这是 AI 的时代 2022 年 没人能预料到 以大模型为代表的人工智能技术 成为这个时代独撑科技行业上行的力量 在这一
  • kubectl exec

    文章目录 kubectl exec 通过bash获得pod中某个容器的TTY 相当于登录容器 命令行 创建一个test文件 kubectl exec exec命令同样类似于docker的exec命令 为在一个已经运行的容器中执行一条shel
  • 模型的“参数”与“超参数”

    目录 前言 一 参数 与 超参数 二 模型训练与最终模型 三 参考文献 前言 起初由于团队项目临时需要 花了个一两天的时间直接仓促上手Machine Learning 最近回顾机器学习的模型评估与选择方面的内容时 才幡然发现在初识机器学习阶
  • 小程序发布后图片不显示问题

    本地图片能正常显示 发布后图片不显示 http 127 0 0 1 49287 pageframe static img apply 20 11 png 修改后 http 127 0 0 1 49287 pageframe static i
  • 获取table中的所有行中的td值,包括input值和select值等

    首先获取表对象document getElementById onceFeeTableId 然后通过循环可以获取所有的行 tableObj rows i 如果有需要可在循环里面再加一个循环 用来循环所有的列 HTML table class
  • 【Mysql 存储过程 Or 存储函数 傻傻分不清? 】

    MySQL的存储函数 自定义函数 和存储过程都是用于存储SQL语句的 但是什么时候用什么呢 是不是总是傻傻的分不清 本文来详细的讲一下存储函数 和存储过程 以后再也不会迷糊 存储函数 存储过程 一 异同点 二 存储函数 语法 三 存储过程
  • Ubuntu 20.04上安装和配置MySql5.7

    此博客作为学习笔记使用 仅供用于学习 勿用于任何非法用途 Ubuntu换源 ubuntu 20 04系统自带源直接安装是MySQL 8 0 我要安装MySQL 5 7的版本的所以先进行换源 1 1 备份原来的sorce文件 sudo cp
  • 深度之眼(四)——Python基本数据类型

  • 使用NPM发布uni-app,Vue自定义组件

    使用NPM发布uni app以及vue自定义组件 使用npm install 安装他人发布的组件平时用的比较多 操作也比较简单 最近自己捣鼓了一下 如何发布自己的自定义组件方便自己和他人使用npm install 安装 编写组件 第一步是本
  • 多线程进阶(下)

    目录 一 JUC 二 线程安全的集合类 三 死锁 一 JUC 这里的juc指的是java util concurrent 并发 多线程相关的 一个标准库中的类 下面是JUC里面的常见类 Callable 这是一个interface 也是一种
  • 样本选择模型 & 处理效应模型

    一 样本选择偏差与自选择偏差 样本选择偏差 样本选择偏差的非随机选择机制在于对样本的选择不随机 在样本数据的采集过程中 只对某部分群体进行调查 但这部分群体与其他群体在某些方面的特征差异较大 因此根据这样的样本做回归得到的普适性结论并不可信
  • iOS自定义下拉刷新控件

    自定义下拉刷新控件 概述 用了很多的别人的下拉刷新控件 想写一个玩玩 自定义一个在使用的时候也会比较有意思 使应用更加的灵动一些 毕竟谁不喜欢各种动画恰到好处的应用呢 使用方式如下 tableview refreshControl XRef
  • 运维实践

    欢迎关注 WeiyiGeek 点击 下方卡片 即可关注我哟 设为 星标 每天带你 基础入门 到 全栈实践 再到 放弃学习 涉及 网络安全运维 应用开发 物联网IOT 学习路径 个人感悟 等知识 花开堪折直须折 莫待无花空折枝 作者主页 ht
  • 初级1 题目二 选择排序

    1 选择排序和冒泡排序很类似 不过冒泡排序是两两比较 每次确定一个元素的位置 而选择排序是一个和其他所有未确定的元素进行比较 然后确定位置 include
  • 如何提高影响力?

    一 什么是影响力 1 通俗的讲 影响力就是影响他人的能力 二 为什么需要提高 1 可以让工作更高效 2 可以让生活更惬意 三 提高影响力的切入点 1 乐于助人与虚心求教 1 1 每个人都会遇到困难 在别人遇到困难的时候 帮助其一下 不要担心
  • js 数组根据字段分组

    在平时的开发中 难免会遇到需要对接口获取的数据进行处理 例如以下数据 const arr a 系列1 b 2 c 3 a 系列2 b 2 c 3 a 系列3 b 2 c 3 我们需要对a字段进行分组 将b和c字段单独取出来 那么我们就可以用
  • 微信小程序注册入口和注册流程(完整版图文教程)

    微信小程序注册是免费的 在腾讯云微信公众平台即可注册 无论是个人 企业都可以免费注册微信小程序 先输入邮箱注册激活 然后填写主体类型对应的证件信息 最后管理员身份验证使用维信扫一扫即可 小程序5来详细说下微信小程序注册入口和微信小程序注册流
  • 下载Android特定模块的源代码

    原文地址 http www apkbus com android 18828 1 1 html 开发中要模仿Android系统中某些应用的特性 需查看其源代码 关于Android源码的介绍见 http source android com
  • Go依赖管理及Go module使用

    目录 一 依赖管理 二 为什么需要依赖管理 三 godep 3 1 安装 3 2 基本命令 3 3 使用godep 3 4 vender机制 3 5 godep开发流程 四 go module 4 1 GO111MODULE 4 2 GOP

随机推荐

  • 周杰伦演唱会总是抢不到票?教你用Python做一个自动抢票脚本

    相信想去周董演唱会的大家都用过大麦网抢票吧 可是 抢不到啊 该说不说 我抢到了 那么 今天带大家用Python来制作一个自动抢票的脚本小程序 知识点 面向对象编程 selenium 操作浏览器 pickle 保存和读取Cookie实现免登陆
  • 微信公众号分享功能 errMsg: “config:fail,Error: 系统错误,错误码:63002,invalid signature [20210130 10:16:32][]“

    H5 分享 请求分享页面时 系统错误 errMsg config fail Error 系统错误 错误码 63002 invalid signature 20210130 10 16 32 解决方案 报错信息 签名错误 需要检查自己的签名是
  • 反转数组(两种方法)

    a 第一种方法 创建一个新数组接收并返回 var arr1 1 2 3 var arr2 reverse1 arr1 console log arr2 console log reverse1 arr1 定义一个新数组 把老数组中的元素反向
  • UsbWebserver + MySQL + Apache的和环境配置问题(主要是连不上MySQL的问题)

    问题1 Apche感叹号 是因为软件路径中含有中文 可以把文件转移到没中文的路径下 问题2 mysqli connect Server sent charset 255 unknown to the client php集成环境 usbwe
  • 电子闹钟设计

    摘要 本设计是以89c51单片机作为控制核心的闹铃系统 本文大致可以分三个章节 第一章讲用单片机制作电子闹钟带来的优势 还有电子闹钟在未来的电子产品中的趋势以及本次设计所要实现的课题目标 第二章讲一些设计思路和硬件组成 第三讲程序代码 最后
  • CVE10大漏洞总结【网络安全】

    1 OpenSSL心脏出血漏洞 漏洞描述 这项严重缺陷 CVE 2014 0160 的产生是由于未能在memcpy 调用受害用户输入内容作为长度参数之前正确进行边界检查 攻击者可以追踪OpenSSL所分配的64KB缓存将超出必要范围的字节信
  • std::string::replace使用小计

    ctrl c ctrl v 真是害死人 看起来简单的东西 往往却又很容易出问题 究其原因 还在于只知其表不知其里 容易想当然 今天遇到的问题是简单调用string replace替换子串的问题 如 std string sTest1 12x
  • web服务器开发课程项目实训,Web前端开发实训案例教程(初级)

    目 录 第1章 实践概述 1 1 1 实践目标 1 1 2 实践知识地图 1 1 3 实施安排 6 1 3 1 实验部分 技术专题 6 1 3 2 综合实践部分 11 第2章 网页设计与制作 19 2 1 实验目标 19 2 2 实验任务
  • FTP命令详解

    FTP命令是Internet用户使用最频繁的命令之一 不论是在DOS还是UNIX操作系统下使用FTP 都会遇到大量的FTP内部命令 熟悉并灵活应用FTP的内部命令 可以大大方便使用者 并收到事半功倍之效 FTP的命令行格式为 ftp v d
  • bytebuffer 使用demo

    pom文件
  • 微信小程序路由

    wx reLaunch Object object 关闭所有页面 打开到应用内的某个页面 一般是跳转到首页使用 例 wx reLaunch url url wx navigateTo Object object 保留当前页面 跳转到应用内的
  • Java时间转换问题 [Failed to convert property value of type ‘java.lang.String‘ to required type ‘java.

    1 错误提示代码 default message Failed to convert property value of type java lang String to required type java 2 分析原因 遇到java接收
  • macOS 系统下安装Lua及lua-cjson

    macOS 系统下安装Lua及lua cjson lua安装及部署 具体操作步骤如下 curl R O http www lua org ftp lua 5 2 3 tar gz tar zxf lua 5 2 3 tar gz cd lu
  • 豆瓣读书top250数据爬取与可视化

    爬虫 scrapy 题目 根据豆瓣读书top250 根据出版社对书籍数量分类 绘制饼图 搭建环境 import scrapy import numpy as np import pandas as pd import matplotlib
  • UE5《Electric Dreams》项目PCG技术解析 之 PCGCustomNodes详解(三)SG_CopyPointsWithHierarchy

    继续解析 Electric Dreams 项目中的自定义节点和子图 SG CopyPointsWithHierarchy和PostCopyPoints OffsetIndices 文章目录 前导文章 标准组合拳 SG CopyPointsW
  • STM32开发中各库函数的主要作用和关系。

    STM32开发中各库函数的主要作用和关系 STM32各库函数关系的简单解析 您好 这是我第一次使用 CSDN来发布文章 如果有排版不合理 结构凌乱 欢迎私信我交流经验 文章内容如有错误 欢迎读者指正 首先我们了解一下什么是库函数 众所周知
  • 常见的几种开源协议

    在学习中经常能看到一些词 例如 GPL LGPL等等 自打上学那会就遇见过 对它们的具体含义却不了解 今天给它们总结一下 说到开源协议 不得不提GNU 课本上给的定义是 GNU is Not Unix 这是官方给出的递归定义 永远也找不到本
  • Linux基础服务3——samba

    文章目录 一 基本了解 1 1 服务安装 1 2 服务进程和端口 1 3 samba用户 1 4 配置文件 1 4 1 主配置文件 1 4 2 配置文件参数 1 5 安全级别 二 访问samba 2 1 参数测试 2 2 交互式访问 2 3
  • 多线程进阶学习10------AQS详解

    AbstractQueuedSynchronizer 来自于JDK1 5 位于JUC包 由并发编程大师Doug Lea编写 字面翻译就是 抽象队列同步器 简称为AQS AQS作为一个抽象类 是构建JUC包中的锁 比如ReentrantLoc
  • Netty工作原理最详细分析

    NIO通讯服务端步骤 1 创建ServerSocketChannel 为它配置非阻塞模式 2 绑定监听 配置TCP参数 录入backlog大小等 3 创建一个独立的IO线程 用于轮询多路复用器Selector 4 创建Selector 将之