• netty零拷贝


    1. 使用DirectBuffer

      以下是NioEventLoop#processSelectedKey方法的部分代码

    1 if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
    2     unsafe.read();
    3     if (!ch.isOpen()) {
    4         // Connection already closed - no need to handle write.
    5         return;
    6     }
    7 }

      unsafe.read()方法对应AbstractNioByteChannel.NioByteUnsafe#read方法。

      该方法的部分代码如下

    1 RecvByteBufAllocator.Handle allocHandle = this.allocHandle;
    2 if (allocHandle == null) {
    3     this.allocHandle = allocHandle = config.getRecvByteBufAllocator().newHandle();
    4 }
    5 // 省略部分代码...
    6 byteBuf = allocator.ioBuffer(byteBufCapacity);
    7 int writable = byteBuf.writableBytes();
    8 int localReadAmount = doReadBytes(byteBuf);

      在执行读操作之前先获取了一个RecvByteBufAllocator.Handle的对象,该对象是用于获取ByteBuf的工具类,可以跟踪代码看到这个工具类是AdaptiveRecvByteBufAllocator#HandleImpl。

      再看工具类的申请byteBuf的方法ioBuffer

    1 public ByteBuf ioBuffer(int initialCapacity) {
    2     if (PlatformDependent.hasUnsafe()) {
    3         return directBuffer(initialCapacity);
    4     }
    5     return heapBuffer(initialCapacity);
    6 }

      这里默认会返回directBuffer。

      获取到byteBuf之后就可以调用doReadBytes方法将数据包读取到byteBuf中。该方法最终会调用到java的socketChannel的read方法,该方法调用IOUtil#read方法来读取数据,来看下该方法的部分代码

    1 if (dst instanceof DirectBuffer)
    2     return readIntoNativeBuffer(fd, dst, position, nd);
    3 // Substitute a native buffer
    4 ByteBuffer bb = Util.getTemporaryDirectBuffer(dst.remaining());

      在开始读取之前先判断byteBuffer是不是DirectBuffer,如果是则直接读取数据,否则需要将byteBuffer拷贝到JVM内存中。

      从以上流程可知netty使用directBuf来实现零拷贝。

    2. 使用CompositeByteBuf

      该类其实就是一个装饰器,将多个byteBuf组装成逻辑上的一个byteBuf,并不存在内存拷贝。详细介绍https://segmentfault.com/a/1190000007560884

    3. 使用FileRegion

      该类是netty的文件传输类,实际上底层是使用的java的NIO提供的transferTo方法将一个channel的数据传输到另一个channel中。

    ps:需要特别注意的是,netty的零拷贝与系统的零拷贝是不同概念,系统层面的零拷贝指的是避免在用户态与内核态间拷贝数据;而netty作为一个应用程序,它只存在于用户态,即使是申请的directBuffer同样是位于用户态。
      socketChannel的数据位于内核态,要把该数据读取到用户态必定会拷贝一次,如果使用directBuffer,则可以直接把数据读取到buffer;如果使用heapBuff,则需要先创建一个directBuffer,然后读取到directBuffer中,然后再从directBuffer读取到目标heapBuff。

    深入了解java directBuffer:

    https://www.zhihu.com/question/57374068/answer/152691891

    netty的directBuf与heapBuf的区别:

    https://my.oschina.net/u/2381372/blog/1476329

  • 相关阅读:
    VC++删除浮动工具条中“关闭”按钮
    automation无法创建对象
    SQL Server 不产生日志
    收缩数据文件
    VB DoEvents用法
    Sql Server添加用户
    Winsock错误代码一览表
    监控数据库性能的sql
    数据库日志文件清理脚本
    VB 中资源文件的多种使用技巧
  • 原文地址:https://www.cnblogs.com/ouhaitao/p/12876128.html
Copyright © 2020-2023  润新知