简单说说 Netty 的零拷贝机制?

Sherwin.Wei Lv7

简单说说 Netty 的零拷贝机制?

回答重点

前置知识:零拷贝的基本理念

  • 避免在用户态和内核态之间拷贝数据,从而降低 CPU 占用和内存带宽的消耗。

  • 除了系统层面的零拷贝,Netty 还强调在 buffer 之间避免不必要的数据拷贝。

Netty 中的零拷贝包括以下四个方面:

零拷贝机制 描述
FileRegion 接口 利用操作系统的 sendfile 系统调用,将文件数据直接从磁盘传输到网络接口,无需经过用户空间,从而实现文件传输的零拷贝。
CompositeByteBuf 将多个 ByteBuf 组合成一个逻辑上的缓冲区,避免在拼接或协议解析过程中发生实际的内存拷贝,提高内存使用效率。
ByteBuf 技术 直接缓冲区(Direct Buffer):使用堆外内存,减少堆内与内核间的数据复制。
包装与切分:使用 Unpooled.wrappedBuffer 包装字节数组,以及 slice 切分大 ByteBuf,这些操作均不产生额外数据拷贝。
内存映射文件 通过 MappedByteBuffer 将文件映射到内存中,使得文件数据可以直接读写,无需额外的数据复制,进而封装成 Netty 的 ByteBuf 用于高效传输。

扩展知识

进一步分析

我们可以从四个方面来看 Netty 中的零拷贝:

1)FileRegion 接口

FileRegion 是 Netty 提供的用于文件传输的接口,它通过调用操作系统的 sendfile 函数实现文件的零拷贝传输。sendfile 函数可以将文件数据直接从文件系统发送到网络接口,而无需经过用户态内存拷贝。

image.png

2)CompositeByteBuf

它是 Netty 提供的一种组合缓冲区,它可以将多个 ByteBuf 实例组合成一个逻辑上的缓冲区,而不需要实际拷贝数据。这种方式可以避免内存拷贝,提高内存使用效率。

1
2
3
4
5
6
7
8
9
10
11
ByteBuf header = Unpooled.buffer();
ByteBuf body = Unpooled.buffer();

header.writeBytes("Header data".getBytes());
body.writeBytes("Body data".getBytes());

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

// Now you can write the compositeByteBuf to the channel without copying data.
channel.writeAndFlush(compositeByteBuf);

3)ByteBuf

ByteBuf 提供了直接缓冲区(Direct Buffer)和堆缓冲区(Heap Buffer)两种类型。

所谓的直接缓冲区就是堆外内存,正常情况下 JVM 使用数据需要将堆外的内存拷贝至堆内中才能访问,而使用了直接缓冲区可以避免在数据传输过程中将数据从 JVM 堆内存拷贝到本地堆内存,从而减少一次数据拷贝。

且可以使用 Unpooled.wrappedBuffer 实现字节数组的包装,即将字节数组包装成 ByteBuf,这个过程也不会产生内存拷贝。

ByteBuf 也可以调用 slice 将一个大 ByteBuf 切分成多个小 ByteBuf,这个拆分操作也避免了内存的拷贝,因为其共享同一个内存区域。

4)内存映射文件(Memory-Mapped File)

Netty 支持使用内存映射文件来实现文件的零拷贝。通过 MappedByteBuffer,文件可以被映射到内存中,并直接进行读取和写入操作,而不需要额外的内存拷贝。

image.png
1
2
3
4
5
6
7
8
9
10
11
File file = new File("path/to/file");
RandomAccessFile raf = new RandomAccessFile(file, "r");
FileChannel fileChannel = raf.getChannel();

MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, file.length());

// Wrap the mapped byte buffer into a Netty ByteBuf
ByteBuf byteBuf = Unpooled.wrappedBuffer(mappedByteBuffer);

// Write the ByteBuf to the channel
channel.writeAndFlush(byteBuf);
Comments
On this page
简单说说 Netty 的零拷贝机制?