深入探秘 Netty、Kafka 中的零拷贝技术!( 三 )

大致意思就是通过 Native 方法获取内存映射的地址,如果失败,手动 GC 再次映射 。
最后通过内存映射的地址实例化出 MappedByteBuffer,MappedByteBuffer 本身是一个抽象类,其实这里真正实例化出来的是 DirectByteBuffer 。
DirectByteBuffer
DirectByteBuffer 继承于 MappedByteBuffer,从名字就可以猜测出开辟了一段直接的内存,并不会占用 JVM 的内存空间 。
上一节中通过 Filechannel 映射出的 MappedByteBuffer 其实际也是 DirectByteBuffer,当然除了这种方式,也可以手动开辟一段空间:
ByteBuffer directByteBuffer = ByteBuffer.allocateDirect(100);如上开辟了 100 字节的直接内存空间 。
Channel-to-Channel 传输
经常需要从一个位置将文件传输到另外一个位置,FileChannel 提供了 transferTo() 方法用来提高传输的效率,首先看一个简单的实例:
public class ChannelTransfer {    public static void main(String[] argv) throws Exception {        String files[]=new String[1];        files[0]="D://db.txt";        catFiles(Channels.newChannel(System.out), files);    }    private static void catFiles(WritableByteChannel target, String[] files)            throws Exception {        for (int i = 0; i < files.length; i++) {            FileInputStream fis = new FileInputStream(files[i]);            FileChannel channel = fis.getChannel();            channel.transferTo(0, channel.size(), target);            channel.close();            fis.close();        }    }}通过 FileChannel 的 transferTo() 方法将文件数据传输到 System.out 通道,接口定义如下:
    public abstract long transferTo(long position, long count,                                    WritableByteChannel target)        throws IOException;几个参数也比较好理解,分别是开始传输的位置,传输的字节数,以及目标通道;transferTo() 允许将一个通道交叉连接到另一个通道,而不需要一个中间缓冲区来传递数据 。
注:这里不需要中间缓冲区有两层意思:第一层不需要用户空间缓冲区来拷贝内核缓冲区,另外一层两个通道都有自己的内核缓冲区,两个内核缓冲区也可以做到无需拷贝数据 。
Netty 零拷贝Netty 提供了零拷贝的 Buffer,在传输数据时,最终处理的数据会需要对单个传输的报文,进行组合和拆分,NIO 原生的 ByteBuffer 无法做到
Netty 通过提供的 Composite(组合)和 Slice(拆分)两种 Buffer 来实现零拷贝 。
看下面一张图会比较清晰:

深入探秘 Netty、Kafka 中的零拷贝技术!

文章插图
 
TCP 层 HTTP 报文被分成了两个 ChannelBuffer,这两个 Buffer 对我们上层的逻辑(HTTP 处理)是没有意义的 。
但是两个 ChannelBuffer 被组合起来,就成为了一个有意义的 HTTP 报文,这个报文对应的 ChannelBuffer,才是能称之为“Message”的东西,这里用到了一个词“Virtual Buffer” 。
可以看一下 Netty 提供的 CompositeChannelBuffer 源码:
【深入探秘 Netty、Kafka 中的零拷贝技术!】public class CompositeChannelBuffer extends AbstractChannelBuffer {    private final ByteOrder order;    private ChannelBuffer[] components;    private int[] indices;    private int lastAccessedComponentId;    private final boolean gathering;    public byte getByte(int index) {        int componentId = componentId(index);        return components[componentId].getByte(index - indices[componentId]);    }    ...省略...


推荐阅读