Netty 中的 Zero-copy 与 OS 层面上的 Zero-copy 不太一样(见/io模块/零拷贝), Netty的 Zero-coyp 完全是在用户态(Java 层面)的, 它的 Zero-copy 的更多的是偏向于 优化数据操作 这样的概念。
零拷贝
- Netty 提供了 CompositeByteBuf 类, 它可以将多个 ByteBuf 合并为一个逻辑上的 ByteBuf, 避免了各个 ByteBuf 之间的拷贝.
- 通过 wrap 操作, 我们可以将 byte[] 数组、ByteBuf、ByteBuffer等包装成一个 Netty ByteBuf 对象, 进而避免了拷贝操作.
- ByteBuf 支持 slice 操作, 因此可以将 ByteBuf 分解为多个共享同一个存储区域的 ByteBuf, 避免了内存的拷贝.
- 通过 FileRegion 包装的FileChannel.tranferTo 实现文件传输, 可以直接将文件缓冲区的数据发送到目标 Channel, 避免了传统通过循环 write 方式导致的内存拷贝问题.
CompositeByteBuf
通过 CompositeByteBuf 实现零拷贝
使用实例
假设有一份协议数据, 由消息头和消息体组成, 而头部和消息体是分别存放在两个 ByteBuf 中,即:
方法将 header 与 body 合并为一个逻辑上的 ByteBuf, 即:
1
2
3
4
5
ByteBuf header = ...
ByteBuf body = ...
CompositeByteBuf compositeByteBuf = Unpooled.compositeBuffer();
compositeByteBuf.addComponents(true, header, body); // 其中第一个参数是 true, 表示当添加新的 ByteBuf 时, 自动递增 CompositeByteBuf 的 writeIndex
定义了一个 CompositeByteBuf 对象, 然后调用:
1
2
3
public CompositeByteBuf addComponents(boolean increaseWriterIndex, ByteBuf... buffers) {
...
}
虽然看起来 CompositeByteBuf 是由两个 ByteBuf 组合而成的, 不过在 CompositeByteBuf 内部, 这两个 ByteBuf 都是单独存在的, CompositeByteBuf 只是逻辑上是一个整体
除了上面直接使用 CompositeByteBuf 类外,还可以使用 Unpooled.wrappedBuffer 方法, 它底层封装了 CompositeByteBuf 操作, 因此使用起来更加方便:
1
2
3
4
ByteBuf header = ...
ByteBuf body = ...
ByteBuf allByteBuf = Unpooled.wrappedBuffer(header, body);
wrap
通过 wrap 操作实现零拷贝
使用实例
使用 Unpooled 的相关方法, 包装这个 byte 数组, 生成一个新的 ByteBuf 实例, 而不需要进行拷贝操作。 代码为:
1
2
byte[] bytes = ...
ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes);
通过 Unpooled.wrappedBuffer 方法将 bytes 包装成一个 UnpooledHeapByteBuf 对象,而在包装的过程中, 是不会有拷贝操作的。 即最后生成的 ByteBuf 对象是和 bytes 数组共用了同一个存储空间, 对 bytes 的修改也会反映到 ByteBuf 对象中。
slice
通过 slice 操作实现零拷贝。slice 操作和 wrap 操作刚好相反, Unpooled.wrappedBuffer 可以将多个 ByteBuf 合并为一个, 而 slice 操作可以将一个 ByteBuf 切片 为多个共享一个存储区域的 ByteBuf 对象。
用 slice 方法产生 header 和 body 的过程是没有拷贝操作的, header 和 body 对象在内部其实是共享了 byteBuf 存储空间的不同部分而已。
ByteBuf 提供了两个 slice 操作方法:
1
2
public ByteBuf slice();
public ByteBuf slice(int index, int length);
FileRegion
Netty 中使用 FileRegion 实现文件传输的零拷贝, 不过在底层 FileRegion 是依赖于 Java NIO FileChannel.transfer 的零拷贝功能。
摘自:博客园