【Netty】Netty 如何实现零拷贝(八)

news2025/2/24 22:26:53

文章目录

  • 前言
  • 一、Java 实现零拷贝
    • 1.1 Java提供 mmap/write 方式
    • 1.2 Java 提供 sendfile 方式
  • 二、Netty 实现零拷贝
    • 2.1 CompositeByteBuf 方式
    • 2.1 wrap 方式
    • 2.3 slice 方式
    • 2.4 FileRegion 方式
  • 总结

前言

回顾Netty系列文章:

  • Netty 概述(一)
  • Netty 架构设计(二)
  • Netty Channel 概述(三)
  • Netty ChannelHandler(四)
  • ChannelPipeline源码分析(五)
  • 字节缓冲区 ByteBuf (六)(上)
  • 字节缓冲区 ByteBuf(七)(下)

本篇文章我们就来讲讲 Netty 的零拷贝,在这之前,我们先来了解一下 Java 是怎么实现零拷贝的。

一、Java 实现零拷贝

Java 实现零拷贝是基于底层操作系统的。就目前而言,Java 支持两种零拷贝技术:mmap/write方式及sendfile方式。

1.1 Java提供 mmap/write 方式

Java NIO 提供的MappedByteBuffer,用于提供mmap/write方式。

Java NlO 中 的Channel (通道)就相当于操作系统中的内核缓冲区,有可能是读缓冲区,也有可能是网络缓冲区,而Buffer就相当于操作系统中的用户缓冲区。

以下是一个MappedByteBuffer的使用案例:

File file = new File("jw.txt");
try {
    FileChannel fc = new RandomAccessFile(file, "rw").getChannel();
    MappedByteBuffer map = fc.map(FileChannel.MapMode.READ_WRITE, 0, file.length());
    map.put("jiangwang".getBytes());
    fc.position(file.length());
    map.clear();
    fc.write(map);
} catch (IOException e) {
    e.printStackTrace();
}

上述示例中,通过FileChannel.map()方法来创建MappedByteBuffer,该方法底层就是调用Linux的 mmap()实现的。

该方法将内核缓冲区的内存和用户缓冲区的内存做了一个地址映射。这种方式适合读取大文件,同时也能对文件内容进行更改,但是如果其后要通过SocketChannel发送,还是需要CPU进行数据的拷贝。

使用MappedByteBuffer,如果是小文件,执行效率不高;而且MappedByteBuffer只能通过调用FileChannel的map()取得,再没有其他方式。因此,Java 中设计MappedByteBuffer就是为大文件准备的。

1.2 Java 提供 sendfile 方式

Java FileChannel.transferTo() 底层实现就是通过 Linux 的 sendfile实现的。该方法直接将当前通道内容传输到另一个通道,没有涉及Buffer的任何操作。

以下是FileChannel.transferTo() 的使用示例:

//使用sendfile:读取磁盘文件,并网络发送
FileChannel sourceChannel = new RandomAccessFile(source, "rw").getChannel();
SocketChannel socketChannel = SocketChannel.open(sa);
sourceChannel.transferTo(0, sourceChannel.size(), socketChannel);

二、Netty 实现零拷贝

Netty 中的零拷贝的实现是基于 Java 的,换言之,底层也是基于操作系统实现的。相对于 Java 中的零拷贝而言,Netty 的零拷贝更多的是偏向于优化数据操作的概念。
Netty 中的零拷贝体现在以下几个方面:

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

从上面几个方法可以看出,前三个方法都是广义零拷贝,其实现方式都是为了减少不必要的数据复制,偏向于应用层数据优化操作。而第四个方法,FileRegion

包装的FileChannel.transferTo(),才是真正的零拷贝(狭义零拷贝)。

下面分别来看其每一种实现。

2.1 CompositeByteBuf 方式

CompositeByteBuf 将多个ByteBuf合并为一个逻辑上的ByteBuf,类似于用一个链表,把分散的多个ByteBuf通过引用连接起来。分散的多个ByteBuf在内存中可能是大小各异、互不相连的区域,通过链表串联起来,作为一块逻辑上的大区域。而在实际数据读取时,还是会去各自每一小块上读取。

下图展示了 CompositeByteBuf 的原理:
在这里插入图片描述

以下是 CompositeByteBuf 使用的代码示例:

ByteBuf header = ...
ByteBuf body = ...
CompositeByteBuf compositeBuffer = Unpooled.compositeBuffer();
compositeBuffer.addComponents(true, header,body);

2.1 wrap 方式

可以通过 wrap操作来实现零拷贝。

通过 wrap 操作,可以将 byte [] 数组、ByteBuf、ByteBuffer等包装成一个 Netty ByteBuf 对象。
例如,通过 Unpooled.wrappedBuffer方法来将 bytes 包装成为一个UnpooledHeapByteBuf对象,而在包装的过程中,是不会有复制操作的。即最后生成的 ByteBuf 对象是和 bytes 数组共用了同一个存储空间,对 bytes 的修改也会反映到 ByteBuf 对象中。

以下是Unpooled.wrappedBuffer使用的代码示例:

ByteBuf header = ...
ByteBuf body = ...
ByteBuf allByteBuf = Unpooled.wrappedBuffer(header,body);

2.3 slice 方式

可以通过 slice 方式实现零拷贝,原理图如下:
在这里插入图片描述

通过 Slice 操作,将ByteBuf分解为多个共享同一个存储区域的ByteBuf。slice 恰好是将一整块区域,划分成逻辑上的独立小区域,在读取每个逻辑上的小区域时,实际会去按 slice(int index,int length)方法中的index和length去读取原内存 buffer 的数据。
以下是 slice 使用的示例代码:

ByteBuf bytebuf = ...
ByteBuf header = bytebuf.slice(0,5);
ByteBuf body = bytebuf.slice(5,10);

2.4 FileRegion 方式

FileRegion底层包装的是 Java 的FileChannel.transferTo()实现文件传输,因此可以直接将文件缓冲区的数据发送到目标Channel。这种方式才是真正操作系统级别的零拷贝。

以下是FileRegion使用的代码示例:

public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
    RandomAccessFile raf = null;
    long length = 0;
    try {
        //1.通过 RandomAccessFile 打开一个文件
        raf = new RandomAccessFile(msg, "r");
        length = raf.length();
    } catch (Exception e) {
        ctx.writeAndFlush("ERR:" + e.getClass() + ": " + e.getMessage());
        return;
    } finally {
        if (length < 0 & raf != null) {
            raf.close();
        }
    }

    ctx.write(raf.length());
    if (ctx.pipeline().get(SslHandler.class) == null) {
        //2.调用 raf.getChannel() 方法获取一个 FileChannel
        //3.将FileChannel封装成一个DefaultFileRegion
        ctx.write(new DefaultFileRegion(raf.getChannel(), 0, length));
    } else {
        ctx.write(new ChunkedFile(raf));
    }
    ctx.write("\n");

}

总结

通过以上的介绍,相信小伙伴们对于Netty的零拷贝机制原理也有了一定的了解,有没有思考一个问题,当我们向缓冲区写入数据时,如果写入的数据超过设置的容量(capacity)怎么办?其实Netty 提供了动态扩容机制,有兴趣的小伙伴们可以自己去了解一下。

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

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

相关文章

李沐多模态串讲笔记

李沐多模态串讲笔记 0.来源1.回顾1.1 ViLT回顾1.2 Clip回顾1.3回顾小结 2.ALBEF2.1摘要2.2主体方法部分2.2.1模型设计2.2.2目标函数2.2.3momentum distillation 动量蒸馏 2.3下游任务和实验结果 3.VLMo3.1论文贡献3.2研究动机3.3主体方法部分3.3.1模型设计3.3.2分阶段的训练策略…

Android 12 通知样式整理

目录 0. &#x1f4c2; 前言 1. &#x1f531; 通知样式总览 2. ⚛️ 通知样式详解 2.1 Simple Notifiaction 2.2 Action Notifiaction 2.3 Remote Input Notifiaction 2.4 Big Picture Notifiaction 2.5 Big Text Notifiaction 2.6 Inbox Notifiaction 2.7 Media No…

Cobalt Strike工具基本使用

Cobalt Strike 安装启动启动server端启动client目标机器连接 工具基使用用户驱动攻击屏幕截图进程列表键盘记录文件管理远程vnc远程代理端口扫描 生成后门被攻击者运行后门文件后查看结果 钓鱼攻击信息收集网站克隆文件下载 安装 网盘地址&#xff1a;链接&#xff1a;https:/…

AntDB-S流式数据库体验

本文作者&#xff1a;彭冲老师&#xff0c;上一篇彭老师体验了亚信刚发布的社区版AntDB-T数据库&#xff0c;文章如下&#xff1a; AntDB-T交易型数据库体验 本文继续体验AntDB-S流式数据库的&#xff0c;AntDB-S目前还未开放社区版&#xff0c;可以联系AntDB小助手进行体验。…

电压放大器的主要指标有哪些方面

电压放大器是电子电路中常用的器件&#xff0c;在选择和评估电压放大器时&#xff0c;需要考虑以下几个主要指标&#xff1a; 输入电阻&#xff08;Input Resistor&#xff09;&#xff1a;输入电阻是指放大器输入端的电阻值&#xff0c;它反映了放大器将输入信号转换成输出信号…

亚马逊,速卖通,国际站卖家在做测评时如何将风险降到最低呢?

测评是亚马逊卖家提升产品可信度和销售表现的重要手段 现在的测评市场遭到卖家们的极力吐槽&#xff0c;想要找到靠谱的资源也越来越难。据了解&#xff0c;去年很多骗子&#xff0c;中介都涌进测评市场&#xff0c;随意报价&#xff0c;导致整个市场鱼龙混杂&#xff0c;卖家…

MyBatis源码学习四之二级缓存

MyBatis源码学习四之二级缓存 MyBatis的缓存使用的也比较多&#xff0c;而缓存的都实现了一个父类的接口Cache。 一、加载缓存类PerputualCache public static void main(String[] args) {InputStream inputStream null;try {inputStream Resources.getResourceAsStream(&q…

NRK3303语音识别芯片在照明灯上的运用,一款可分布式语音IC方案

随着科技的不断进步&#xff0c;人们对于家居生活中的照明设备的要求也逐渐提高。传统的照明方式已经不能满足人们对智能家居的需求&#xff0c;我们需要更加智能、易于操作、高效节能的智能化照明系统。因此&#xff0c;智能照明应运而生&#xff0c;为我们提供了更加智能化、…

倍福触摸屏维修控制面板CP6606-0001-0020

适合控制柜安装的CP6600和CP6606面板型PC&#xff0c;适用于机械制造和设备工程等各种应用&#xff1b;可以安装TwinCAT自动化软件和 Windows Embedded操作系统&#xff0c;用作单独的控制器&#xff0c;也可以用作远程桌面显示器。 倍福触摸屏常见故障分类&#xff1a; 1、磨…

linux学习[11]磁盘与文件系统(2):lsblkblkidpartedfdiskgdiskmkfs

文章目录 前言&#xff1a;1. 磁盘容量1.1 lsblk1.2 blkid1.3 parted 2. 磁盘分区2.1 fdisk/gdisk2.2 磁盘分区实例参考&#xff1a; 3. 磁盘格式化3.1 mkfs.xfs3.2 mkfs.ext43.3 mkfs.vfat 总结&#xff1a; 前言&#xff1a; 写了VMware的磁盘扩容之后&#xff0c;磁盘分区格…

深度学习基础-卷积神经网络CNN+深度学习(无代码仅理解)

参考书籍&#xff1a;&#xff08;找不到资源可以后台私信我&#xff09; 《深度学习入门&#xff1a;基于Python的理论与实现 (斋藤康毅)》 CNN 概括 其中pooling层有时候会被省略&#xff0c;卷积层的输入输出图像称为特征图&#xff08;feature map&#xff09;&#xff0c…

多线程-Thread类的常用方法和生命周期

Thread类的常用结构 构造器 public Thread():分配一个新的线程对象。public Thread(String name):分配一个指定名字的新的线程对象。public Thread(Runnable target):指定创建线程的目标对象&#xff0c;它实现了Runnable接口中的run()方法。public Thread(Runnable target,S…

Python实现温度植被干旱指数(TVDI)的计算

前言 温度植被干旱指数&#xff08;Temperature Vegetation Dryness Index&#xff0c;TVDI&#xff09;是一种基于光学与热红外遥感通道数据进行植被覆盖区域表层土壤水分反演的方法。作为同时与归一化植被指数(NDVI)和地表温度(LST)相关的温度植被干旱指数(TVDI)可用于干旱监…

第二十五节:通信之WLAN(WiFi聚合)

欢迎大家一起学习探讨通信之WLAN。为了减少帧交互中额外资源占用开销&#xff0c;提高WiFi网络系统整体运行效率&#xff0c;802.11n协议引入定义了聚合功能。本节将基于协议定义内容和实例&#xff0c;详细分析“A-MSDU"和“A-MPDU”两种聚合功能。 关键字 S1G(Sub 1 GH…

linux0.12-10-6-tty_io.c

[539页] 10-6 tty_io.c程序 10-6-1 功能描述 每个tty设备有3个缓冲队列&#xff0c;分别是读缓冲队列(read_q)、写缓冲队列(write_q)和辅助缓冲队列(secondary)&#xff0c;定义在tty_struct结构中(include/linux/tty.h)。 对于每个缓冲队列&#xff0c;读操作是从缓冲队列的…

数据可视化:部分整体类可视化图表大全

图表是处理数据的重要组成部分&#xff0c;因为它们是一种将大量数据压缩为易于理解的格式的方法。数据可视化可以让受众快速Get到重点。 数据可视化的图表类型极其丰富多样&#xff0c;而且每种都有不同的用例&#xff0c;通常&#xff0c;创建数据可视化最困难的部分是确定哪…

冯诺依曼体系结构详解

一.冯诺伊曼体系结构的概念&#xff1a; 约翰冯诺依曼&#xff08;John von Neumann&#xff0c;1903.1.28-1957.2.8&#xff09;&#xff0c;美籍匈牙利数学家&#xff0c;计算机科学家&#xff0c;物理学家。是20世纪最重要的数学家之一&#xff0c;后来被称为计算机之父。 后…

计算机网络学习笔记-网络层

目录 概述 提供的两种服务&#xff1a;面向连接的虚电路、不面向连接的数据报 对比 虚拟互连网络 地址解析协议 ARP 主要作用 使用过程 位置 因特网控制报文协议 ICMP 作用 位置 种类 差错报告报文&#xff1a;终点不可达、源点抑制、时间超过、参数问题、改变路由…

【HMS Core】【ML Kit】活体检测FAQ合集

【问题描述1】 使用示例代码集成活体检测SDK时&#xff0c;报错state code -7001 【解决方案】 使用示例代码前请详细阅读示例工程中的“README”文件。您需要完成以下操作后才可以运行示例代码。 在AppGallery Connect网站下载自己应用的“agconnect-services.json”文件&a…