[关闭]
@adamhand 2019-01-08T22:42:09.000000Z 字数 2010 阅读 1163

Netty系列(5)--零拷贝


简述

Netty中的零拷贝包括两种:OS级别的和应用程序级别的。

Java NIO中的FileChannel.transferTo()方法都实现了零拷贝的功能,在Netty中也通过在FileRegion中包装了NIO的FileChannel.transferTo()方法实现了零拷贝。所以,OS级别的零拷贝是通过transferTo()实现的。

应用程序级别的零拷贝主要是通过CompositeByteBuf等实现的。

OS级别的零拷贝

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

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

举一个例子,假如要将一个磁盘中的文件通过Socket发送,使用的伪代码如下:

  1. `File.read(bytes)`
  2. `Socket.send(bytes)`

如果不适用零拷贝,需要四次数据拷贝和四次上下文切换:

如下图所示:



可以看到,2操作和3操作的两次复制其实是没有必要的。如果使用FileChannel.transferTo方法,可以避免上述的两个多余操作。如下图所示



这样,就避免了在内核和应用程序之间不必要的复制操作。

应用级别的零拷贝

应用界别的Zero-copy 体现在如下几个个方面:

通过 CompositeByteBuf 实现零拷贝

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

  1. ByteBuf header = ...
  2. ByteBuf body = ...

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

  1. ByteBuf allBuf = Unpooled.buffer(header.readableBytes() + body.readableBytes());
  2. allBuf.writeBytes(header);
  3. allBuf.writeBytes(body);
  4. ````
  5. 可以看到, 我们将 header body 都拷贝到了新的 allBuf 中了, 这无形中增加了两次额外的数据拷贝操作了.
  6. 那么有没有更加高效优雅的方式实现相同的目的呢? 我们来看一下 CompositeByteBuf 是如何实现这样的需求的吧.
  7. <div class="md-section-divider"></div>
  8. ```java
  9. ByteBuf header = ...
  10. ByteBuf body = ...
  11. CompositeByteBuf compositeByteBuf = Unpooled.compositeBuffer();
  12. compositeByteBuf.addComponents(true, header, body);

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

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

方法将 header 与 body 合并为一个逻辑上的 ByteBuf。不过在 CompositeByteBuf 内部, 这两个 ByteBuf 都是单独存在的。

待续......

参考

对于 Netty ByteBuf 的零拷贝(Zero Copy)的理解
Netty中的零拷贝
理解Netty中的零拷贝(Zero-Copy)机制
netty学习十三:零拷贝底层实现原理
Java-NIO(三):直接缓冲区与非直接缓冲区
Netty 系列之 Netty 高性能之道
Netty in action—Netty中的ByteBuf

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注