对于 Netty ByteBuf 的零拷贝(Zero Copy) 的理解

1 篇文章 0 订阅
订阅专栏

原文:对于 Netty ByteBuf 的零拷贝(Zero Copy) 的理解

根据 Wiki 对 Zero-copy 的定义:

“Zero-copy” describes computer operations in which the CPU does not perform the task of copying data from one memory area to another. This is frequently used to save CPU cycles and memory bandwidth >when transmitting a file over a network.

即所谓的 Zero-copy, 就是在操作数据时, 不需要将数据 buffer 从一个内存区域拷贝到另一个内存区域. 因为少了一次内存的拷贝, 因此 CPU 的效率就得到的提升.

在 OS 层面上的 Zero-copy 通常指避免在 用户态(User-space) 与 内核态(Kernel-space) 之间来回拷贝数据. 例如 Linux 提供的 mmap 系统调用, 它可以将一段用户空间内存映射到内核空间, 当映射成功后, 用户对这段内存区域的修改可以直接反映到内核空间; 同样地, 内核空间对这段区域的修改也直接反映用户空间. 正因为有这样的映射关系, 我们就不需要在 用户态(User-space) 与 内核态(Kernel-space) 之间拷贝数据, 提高了数据传输的效率.

而需要注意的是, Netty 中的 Zero-copy 与上面我们所提到到 OS 层面上的 Zero-copy 不太一样, Netty的 Zero-coyp 完全是在用户态(Java 层面)的, 它的 Zero-copy 的更多的是偏向于 优化数据操作 这样的概念.

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

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

下面我们就来简单了解一下这几种常见的零拷贝操作.

通过 CompositeByteBuf 实现零拷贝

假设我们有一份协议数据, 它由头部和消息体组成, 而头部和消息体是分别存放在两个 ByteBuf 中的, 即:

ByteBuf header = ...
ByteBuf body = ...

我们在代码处理中, 通常希望将 header 和 body 合并为一个 ByteBuf, 方便处理, 那么通常的做法是:

ByteBuf allBuf = Unpooled.buffer(header.readableBytes() + body.readableBytes());
allBuf.writeBytes(header);
allBuf.writeBytes(body);

可以看到, 我们将 header 和 body 都拷贝到了新的 allBuf 中了, 这无形中增加了两次额外的数据拷贝操作了.

那么有没有更加高效优雅的方式实现相同的目的呢? 我们来看一下 CompositeByteBuf 是如何实现这样的需求的吧.

ByteBuf header = ...
ByteBuf body = ...

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

上面代码中, 我们定义了一个 CompositeByteBuf 对象, 然后调用

public CompositeByteBuf addComponents(boolean increaseWriterIndex, ByteBuf... buffers) {
...
}

方法将 header 与 body 合并为一个逻辑上的 ByteBuf, 即:

image

不过需要注意的是, 虽然看起来 CompositeByteBuf 是由两个 ByteBuf 组合而成的, 不过在 CompositeByteBuf 内部, 这两个 ByteBuf 都是单独存在的, CompositeByteBuf 只是逻辑上是一个整体.

上面 CompositeByteBuf 代码还以一个地方值得注意的是, 我们调用 addComponents(boolean increaseWriterIndex, ByteBuf... buffers)来添加两个 ByteBuf, 其中第一个参数是 true, 表示当添加新的 ByteBuf 时, 自动递增 CompositeByteBuf 的 writeIndex.
如果我们调用的是

compositeByteBuf.addComponents(header, body);

那么其实 compositeByteBuf 的 writeIndex 仍然是0, 因此此时我们就不可能从 compositeByteBuf 中读取到数据, 这一点希望大家要特别注意.

除了上面直接使用 CompositeByteBuf 类外, 我们还可以使用 Unpooled.wrappedBuffer 方法, 它底层封装了 CompositeByteBuf 操作, 因此使用起来更加方便:

ByteBuf header = ...
ByteBuf body = ...

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

通过 wrap 操作实现零拷贝

例如我们有一个 byte 数组, 我们希望将它转换为一个 ByteBuf 对象, 以便于后续的操作, 那么传统的做法是将此 byte 数组拷贝到 ByteBuf 中, 即:

byte[] bytes = ...
ByteBuf byteBuf = Unpooled.buffer();
byteBuf.writeBytes(bytes);

显然这样的方式也是有一个额外的拷贝操作的, 我们可以使用 Unpooled 的相关方法, 包装这个 byte 数组, 生成一个新的 ByteBuf 实例, 而不需要进行拷贝操作. 上面的代码可以改为:

byte[] bytes = ...
ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes);

可以看到, 我们通过 Unpooled.wrappedBuffer 方法来将 bytes 包装成为一个 UnpooledHeapByteBuf 对象, 而在包装的过程中, 是不会有拷贝操作的. 即最后我们生成的生成的 ByteBuf 对象是和 bytes 数组共用了同一个存储空间, 对 bytes 的修改也会反映到 ByteBuf 对象中.

Unpooled 工具类还提供了很多重载的 wrappedBuffer 方法:

public static ByteBuf wrappedBuffer(byte[] array)
public static ByteBuf wrappedBuffer(byte[] array, int offset, int length)

public static ByteBuf wrappedBuffer(ByteBuffer buffer)
public static ByteBuf wrappedBuffer(ByteBuf buffer)

public static ByteBuf wrappedBuffer(byte[]... arrays)
public static ByteBuf wrappedBuffer(ByteBuf... buffers)
public static ByteBuf wrappedBuffer(ByteBuffer... buffers)

public static ByteBuf wrappedBuffer(int maxNumComponents, byte[]... arrays)
public static ByteBuf wrappedBuffer(int maxNumComponents, ByteBuf... buffers)
public static ByteBuf wrappedBuffer(int maxNumComponents, ByteBuffer... buffers)

这些方法可以将一个或多个 buffer 包装为一个 ByteBuf 对象, 从而避免了拷贝操作.

通过 slice 操作实现零拷贝

slice 操作和 wrap 操作刚好相反, Unpooled.wrappedBuffer 可以将多个 ByteBuf 合并为一个, 而 slice 操作可以将一个 ByteBuf 切片 为多个共享一个存储区域的 ByteBuf 对象.
ByteBuf 提供了两个 slice 操作方法:

public ByteBuf slice();
public ByteBuf slice(int index, int length);

不带参数的 slice 方法等同于 buf.slice(buf.readerIndex(), buf.readableBytes()) 调用, 即返回 buf 中可读部分的切片. 而 slice(int index, int length) 方法相对就比较灵活了, 我们可以设置不同的参数来获取到 buf 的不同区域的切片.

下面的例子展示了 ByteBuf.slice 方法的简单用法:

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

用 slice 方法产生 header 和 body 的过程是没有拷贝操作的, header 和 body 对象在内部其实是共享了 byteBuf 存储空间的不同部分而已. 即:

image

通过 FileRegion 实现零拷贝

Netty 中使用 FileRegion 实现文件传输的零拷贝, 不过在底层 FileRegion 是依赖于 Java NIO FileChannel.transfer 的零拷贝功能.

首先我们从最基础的 Java IO 开始吧. 假设我们希望实现一个文件拷贝的功能, 那么使用传统的方式, 我们有如下实现:

public static void copyFile(String srcFile, String destFile) throws Exception {
    byte[] temp = new byte[1024];
    FileInputStream in = new FileInputStream(srcFile);
    FileOutputStream out = new FileOutputStream(destFile);
    int length;
    while ((length = in.read(temp)) != -1) {
        out.write(temp, 0, length);
    }

    in.close();
    out.close();
}

上面是一个典型的读写二进制文件的代码实现了. 不用我说, 大家肯定都知道, 上面的代码中不断中源文件中读取定长数据到 temp 数组中, 然后再将 temp 中的内容写入目的文件, 这样的拷贝操作对于小文件倒是没有太大的影响, 但是如果我们需要拷贝大文件时, 频繁的内存拷贝操作就消耗大量的系统资源了.
下面我们来看一下使用 Java NIO 的 FileChannel 是如何实现零拷贝的:

public static void copyFileWithFileChannel(String srcFileName, String destFileName) throws Exception {
    RandomAccessFile srcFile = new RandomAccessFile(srcFileName, "r");
    FileChannel srcFileChannel = srcFile.getChannel();

    RandomAccessFile destFile = new RandomAccessFile(destFileName, "rw");
    FileChannel destFileChannel = destFile.getChannel();

    long position = 0;
    long count = srcFileChannel.size();

    srcFileChannel.transferTo(position, count, destFileChannel);
}

可以看到, 使用了 FileChannel 后, 我们就可以直接将源文件的内容直接拷贝(transferTo) 到目的文件中, 而不需要额外借助一个临时 buffer, 避免了不必要的内存操作.

有了上面的一些理论知识, 我们来看一下在 Netty 中是怎么使用 FileRegion 来实现零拷贝传输一个文件的:

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

    ctx.write("OK: " + raf.length() + '\n');
    if (ctx.pipeline().get(SslHandler.class) == null) {
        // SSL not enabled - can use zero-copy file transfer.
        // 2. 调用 raf.getChannel() 获取一个 FileChannel.
        // 3. 将 FileChannel 封装成一个 DefaultFileRegion
        ctx.write(new DefaultFileRegion(raf.getChannel(), 0, length));
    } else {
        // SSL enabled - cannot use zero-copy file transfer.
        ctx.write(new ChunkedFile(raf));
    }
    ctx.writeAndFlush("\n");
}

上面的代码是 Netty 的一个例子, 其源码在 netty/example/src/main/java/io/netty/example/file/FileServerHandler.java
可以看到, 第一步是通过 RandomAccessFile 打开一个文件, 然后 Netty 使用了 DefaultFileRegion 来封装一个 FileChannel 即:

new DefaultFileRegion(raf.getChannel(), 0, length)

当有了 FileRegion 后, 我们就可以直接通过它将文件的内容直接写入 Channel 中, 而不需要像传统的做法: 拷贝文件内容到临时 buffer, 然后再将 buffer 写入 Channel. 通过这样的零拷贝操作, 无疑对传输大文件很有帮助.

Netty零拷贝
Deronn的博客
09-05 1195
Netty零拷贝
NettyByteBuf零拷贝
weixin_30807677的博客
02-20 335
转载:https://www.jianshu.com/p/1d1fa2fe1ed9 此文章已同步发布在我的segmentfault专栏. 根据 Wiki 对 Zero-copy 的定义: "Zero-copy" describes computer operations in which the CPU does not perform the task of copying...
理解Netty中的零拷贝Zero-Copy)机制
weixin_34178244的博客
01-13 304
2019独角兽企业重金招聘Python工程师标准>>> ...
理解Netty中的Zero-copy
weixin_34349320的博客
04-11 151
2019独角兽企业重金招聘Python工程师标准>>> ...
Netty ByteBuf零拷贝(Zero Copy)
weixin_38852633的博客
06-08 221
引言: 所谓的 Zero-copy, 就是在操作数据时, 不需要将数据 buffer 从一个内存区域拷贝到另一个内存区域. 因为少了一次内存的拷贝, 因此 CPU 的效率就得到的提升. 转载原文:https://www.cnblogs.com/xys1228/p/6088805.html ...
Netty ByteBuf 零拷贝
shudo_liu的博客
07-11 850
转自http://www.cnblogs.com/xys1228/p/6088805.html 根据 Wiki 对 Zero-copy 的定义: "Zero-copy" describes computer operations in which the CPU does not perform the task of copying data from one memory
Netty ByteBuf零拷贝(Zero Copy)详解
深圳市多克创新科技有限公司
02-16 658
Netty ByteBuf零拷贝(Zero Copy)详解
对于 Netty ByteBuf零拷贝(Zero Copy) 的理解1
08-03
Netty ByteBuf零拷贝(Zero-Copy)理解 Netty 中的零拷贝Zero-Copy)是指在操作数据时,不需要将数据 buffer 从一个内存区域拷贝到另一个内存区域,这样可以减少 CPU 的负载和内存带宽的占用。 Zero-Copy 通常...
Netty ByteBuf零拷贝原理与应用深度解析
Netty ByteBuf零拷贝Zero Copy)是一种高级性能优化技术,旨在减少数据在内存和操作系统之间的复制操作,从而提高网络通信的效率。在传统的网络编程中,数据在用户态(应用程序)和内核态(操作系统)之间频繁...
Netty基础】Netty的“零拷贝
热门推荐
白夜行
06-16 1万+
零拷贝的定义 Zero-copy, 就是在操作数据时, 不需要将数据 buffer 从一个内存区域拷贝到另一个内存区域. 因为少了一次内存的拷贝, 因此 CPU 的效率就得到的提升. 在 OS 层面上的 Zero-copy 通常指避免在 用户态(User-space) 与 内核态(Kernel-space) 之间来回拷贝数据。 但Netty 中的 Zero-copy 与 OS 的 Zero-copy
Netty零拷贝(Zero-Copy)
CodingAnHour
07-31 2774
Netty提供CompositeByteBuf组合缓冲区类,可以将多个ByteBuf合并为一个逻辑上的ByteBufer,避免了各个ByteBufer之间的拷贝,将几个小buffer合并成一个大buffer的繁琐操作。Netty中的零拷贝和操作系统层面上的零拷贝是有区别的,不能混淆,Netty零拷贝完全是基于Java层面或者说用户空间的,它更多的是偏向于应用中的数据操作优化,而不是系统层面的操作优化。Netty零拷贝(Zero-Copy)主要体现在五个方面。,使用堆外直接内存进行Socket读写,...
Netty零拷贝zero-copy
记录各种Bug解决案例及日常学习内容
05-10 567
目录1. 零拷贝技术实现2. 传统读取IO流的操作2.1 读操作2.2 写操作2.3 MMAP+write2.4 Sendfile3. 零拷贝应用场景 很多更新的技术在宣传的时候,都会提到速度、性能这个指标,什么提升了10倍,100倍啊,其中有一个技术点叫做zero-copy,也叫做零拷贝零拷贝核心优化点:就需要减少「用户态与内核态的上下文切换」和「内存拷贝」的次数。 1. 零拷贝技术实现 1.直接 I/O 2.mmap 3.sendfile 2. 传统读取IO流的操作 2.1 读操作 1、应用程序发
Netty: 零拷贝Zero-copy)技术
最新发布
玉汝于成
05-22 1186
零拷贝Zero-copy)技术是指计算机执行操作时,CPU不需要先将数据从某处内存复制到另一个特定区域。这种技术通常用于通过网络传输文件时节省CPU周期和内存带宽。零拷贝技术的主要原理是通过减少数据在用户空间与内核空间之间切换模式的次数,以及减少数据的拷贝次数,来提高数据传输效率。
Netty中的零拷贝
sqliu301190的博客
07-31 214
Netty 零拷贝 sendFile DMA
Netty零拷贝ByteBuf
程序员路同学
10-09 683
ByteBufNetty框架封装的数据缓冲区,区别于ByteBuffer需要有position、limit、flip等属性和操作来控制byteBuffer数据读写,Bytebuf通过两个位置指针来协助缓冲区的读写操作,分别是readIndex和writerIndex。缓冲区有两种模式,分别是写模式和读模式,这两种模式通过使用flip方法进行模式。Netty零拷贝完全是在用户态(Java 层面)的, 它的零拷贝更多的是偏向于 优化数据操作 这样的概念.而不是操作系统层面的用户态和核心态之间的数据拷贝。
Netty三大要素,ByteBuf零拷贝机制、职责链
sinat_28979467的博客
10-23 432
//1、创建非池化的ByteBuf 大小设置为10 ByteBuf byteBuf= Unpooled.buffer(10); //原始ByteBuf*************>UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 0, cap: 10) ...
零拷贝机制
qianhuan_的博客
01-06 262
Netty零拷贝机制是一种应用层的实现。 CompositeByteBuf,讲多个ByteBuf 合并为逻辑上的ByteBuf ,避免了多个ByteBuf之间的拷贝(如果是两个数组拷贝,那么可能先申请大的数组,将两个小的数组复制过来),但是ByteBuf提供了虚拟的复合缓冲区,逻辑上将多个ByteBuf合并到一起,实际上,数据没有动 wrapedBuffer 将byte[] 包装成ByteBuf对象,字节数组的地址不变,ByteBuf引用字节数组的地址 slice拆分,将大的ByteBuf 拆分成几
Netty中的零拷贝机制
提供各种学习资源及帮助
06-20 604
等一下,不是说零拷贝吗?为什么还是要 2 次拷贝?首先我们说零拷贝,是从操作系统的角度来说的。因为内核缓冲区之间,没有数据是重复的(只有 kernel buffer 有一份数据,sendFile 2.1 版本实际上有 2 份数据,算不上零拷贝)。例如我们刚开始的例子,内核缓存区和 Socket 缓冲区的数据就是重复的。而零拷贝不仅仅带来更少的数据复制,还能带来其他的性能优势,例如更少的上下文切换,更少的 CPU 缓存伪共享以及无 CPU 校验和计算。再稍微讲讲 mmap 和 sendFile 的区别。
写文章

热门文章

  • JVM参数设置 50163
  • 关于ESAPI获取资源文件问题 17553
  • IDEA配置ssh拉取Github代码 13324
  • Java实现拖动图片验证处理 12182
  • JAVA类加载器 11273

分类专栏

  • 面试
  • 大数据 9篇
  • 区块链 2篇
  • IDEA 2篇
  • Gradle 1篇
  • 数据库 7篇
  • 个人随笔 5篇
  • JVM 7篇
  • 网络 4篇
  • Spring 3篇
  • 并发 10篇
  • 安全编程 1篇
  • Java 1篇
  • redis 3篇
  • zookeeper 2篇
  • solr 1篇
  • VMware 1篇
  • netty 1篇
  • linux 1篇
  • maven 1篇

最新评论

  • JVM参数设置

    程光CS: -XX:PermSize在jdk1.8已经废弃了吧

  • Java实现拖动图片验证处理

    lianganton: 可以看看 https://blog.csdn.net/lianganton/article/details/125997429?spm=1001.2014.3001.5502

  • JVM参数设置

    凉月啊八: [code=plain] -Xms设置堆的最小空间大小。 [/code] 这里写错了,应该是初始空间

  • Flink集群运行问题小记

    Alvin家鸡鸭鱼的小米米: 分析的过程是对的,但是结论是错的,java确实是使用双亲委派classloader,但是flink为了解决依赖问题,默认是child-first load模式,也就是一部分基础类依然是双亲委派,另外一部分第三方包是是直接使用用户UserCodeClassLoader率先加载的

  • JVM参数设置

    程序猴小毛: 关键是这些参数在哪设置啊

大家在看

  • AOP武功秘籍:用@EnableAspectJAutoProxy让Spring Boot焕发新生

最新文章

  • Flink集群运行问题小记
  • 堆外内存分析
  • 发布Maven构件到中央仓库
2019年11篇
2018年53篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

玻璃钢生产厂家玻璃钢仿铜雕塑效果图南昌公园玻璃钢雕塑定做九龙坡玻璃钢人物雕塑金昌玻璃钢动物雕塑设计河北中庭商场美陈价格四会玻璃钢造型雕塑徐州玻璃钢雕塑人物设计广场玻璃钢人物雕塑价格合理河北开业商场美陈多少钱玻璃钢海豚景观雕塑定制福建中庭商场美陈费用太原创意玻璃钢雕塑大型玻璃钢雕塑买哪家好定做蛋型玻璃钢花盆昆明市玻璃钢雕塑厂家电话玻璃钢蒙元文化雕塑玻璃钢泡沫雕塑制造厂家绍兴景区玻璃钢雕塑销售厂家张家界校园玻璃钢雕塑通渭玻璃钢雕塑供应下城区玻璃钢雕塑尺寸西安玻璃钢大型雕塑专研商场美陈灯光北京商场美陈厂家供应赛罕区玻璃钢雕塑房地产玻璃钢人物雕塑设计山东抽象玻璃钢雕塑价位江都玻璃钢气球雕塑甘肃卡通玻璃钢雕塑设计及定制南通小提琴玻璃钢花盆香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化