Netty-ByteBuf

1,116 阅读5分钟

特性

  • ByteBuf是Netty中网络传输数据的字节缓冲。
  • ByteBuf是一个可以随机、顺序访问的字节序列。
  • 底层存储基于Byte[](HeapBuffer)与nio中的ByteBuffer(DirectBuffer)实现。
  • 与nio.ByteBuffer相比主要特性有:
    1. 支持顺序读写操作,无需手动切换。ByteBuffer中只维护了pos指针,读/写操作需要开发者先手动调用filp()/clear()来切换指针位置,再进行操作,而ByteBuf内部分别维护了读写指针(readIndex、markedReadIndex)/(writeIndex、markedWriteIndex),互不干扰,因此开发者可以自由的使用read、write方法,无需关心读写模式切换。
    2. 可以通过nioBuffer()方法获取相同内容的ByteBuffer,注意:对ByteBuffer的读写操作将不会影响原ByteBuf;如果原ByteBuf是动态buffer的话,对原ByteBuf的修改操作(调整容量等)也不会影响ByteBuffer。

示例

  • create
    ByteBuf buf = Unpooled.buffer(1024);//创建一个基于bytep[1024]的ByteBuf
    ByteBuf buf = Unpooled.directBuffer(1024);//创建一个基于ByteByffer的ByteBuf(直接内存)
    ByteBuf buf = Unpooled.wrappedBuffer(new Byte[1204]);//将一个byte[]包装成ByteBuf,byte[]的变动对ByteBuf可见
    ByteBuf buf = Unpooled.wrappedBuffer(ByteBuffer.allocate(1024));//将一个ByteByffer包装成ByteBuf,ByteByffer的变动对ByteBuf可见
    ByteBuf buf = Unpooled.wrappedBuffer(Unpooled.buffer(1024));//将一个ByteBuf包装成ByteBuf,ByteBuf的变动对ByteBuf可见
    ByteBuf buf = Unpooled.copiedBuffer(Unpooled.buffer(1024));//copy一个byte[]并创建ByteBuf,byte[]的变动对ByteBuf不可见
    ...
    
  • read
    //访问指定位置的byte
    ByteBuf buf = Unpooled.buffer(1024);
    for(int i = 0;i<buf.capacity();i++){
        Byte b = buf.getByte(i);
    }
    
    //顺序访问
    ByteBuf buf = Unpooled.buffer(1024);
    while(buf.readable()){
        Byte b = buf.readByte();
    }
    
  • write
    ByteBuf buf = Unpooled.buffer(1024);
    while(buf.maxWriteableBytes() >= 4 ){
        buf.writeInt(new Random().nextInt());
    }
    //buf.writeBoolean(boolean b);
    //buf.writeByte(Byte b);
    //buf.writeLong(long l);
    //buf.writeShort(short s);
    //buf.writeChar(char c);
    //buf.writeFloat(float f);
    //buf.writeDouble(double d);
    //buf.writeMedium(int i);
    
  • clear
    ByteBuf buf = Unpooled.buffer(1024);
    //将readIndex和writeIndex都置为0
    buf.clear();
    

核心流程

ByteBuf是Netty框架中网络传输的基本数据结构,涉及的核心流程是数据存储空间的分配和释放。ByteBuf又分为HeapBuffer(堆缓冲,基于java虚拟机内存堆的)和DirectBuffer(直接缓冲,基于系统内存的,详情请了解nio中的DirectByteBuffer),要了解ByteBuf的分配和释放,首先要了解两种缓冲模型的区别。

  • HeapBuffer
    • 在jvm管理的内存堆上分配,底层通过byte[]实现,可以理解为byte数组的包装类,jvm负责回收释放(GC)。
    • 优势:分配和回收效率更高。
    • 缺点:linux系统发生网络读写时,会先将网络数据从连接copy到内核缓冲,再从内核缓冲(系统空间)copy到jvm内存(用户空间)中,需要两次copy,在大数据、高并发的场景下会影响性能,造成内存浪费。
  • DirectBuffer
    • 在jvm管理的内存外分配(系统内存),通过MMAP技术映射一块内存给当前应用程序。内存块的回收不由jvm控制,jvm只负责回收映射内存块的对象,当对象被回收后,操作系统会释放对应内存块。
    • 优势:由于DirectBuffer是通过MMAP管理一块系统内存,相对于HeapBuffer,在发生网络读写时,DirectBuffer只需要一次copy:从网络连接copy到内核缓冲(系统空间),DriectBuffer通过映射(MMAP)可以直接操作对应的内存,减少系统不必要的开销,降低内存使用。
    • 缺点:由于DirectBuffer所使用的内存在jvm内存之外,所以分配和释放的代价相比HeapBuffer来说更高。
  • 总结
    • 基于两种buffer的特性得出以下结论:
      1. DirectBuffer更适用于网络数据读写,尤其是数据量大、传输持久的场景,并且尽量复用(缓冲池)。
      2. HeapBuffer适用于对数据的业务操作,在日常开发中大部分场景都可以直接使用HeapBuffer。

了解了两种缓冲模型,下面我们了解Netty对于ByteBuf的分配是怎么做的。

  • Netty提供了一系列工具类辅助开发者管理ByteBuf:
    • 核心接口:ByteBufAllocator
      Method desc
      ByteBuf buffer() 分配一个ByteBuf,heap或direct由实现决定
      ByteBuf buffer(int initialCapacity) 分配一个指定初始容量的Bytebuf,heap或direct由实现决定
      ByteBuf buffer(int initialCapacity, int maxCapacity) 分配一个指定初始容量和最大容量的ByteBuf,heap或direct由实现决定
      ByteBuf ioBuffer() 分配一个用于io操作的ByteBuf,最好是direct buffer
      Bytebuf ioBuffer(int initialCapacity) 分配一个指定初始化容量的用于io操作的ByteBuf,最好是direct buffer
      ByteBuf ioBuffer(int intialCapacity, int maxCapacity) 分配一个指定初始化容量和最大容量的用户io操作的ByteBuf,最好是direct buffer
      ByteBuf heapBuffer() 分配一个heap buffer
      ByteBuf heapBuffer(int initialCapacity) 分配一个指定初始化容量的heap buffer
      ByteBuf heapBuffer(int initialCapacity, int maxCapacity) 分配一个指定初始化容量和最大容量的heap buffer
      ByteBuf directBuffer() 分配一个direct buffer
      ByteBuf directBuffer(int initialCapacity) 分配一个指定初始化容量的direct buffer
      ByteBuf directBuffer(int initialCapacity, int maxCapacity) 分配一个指定初始化容量和最大容量的direct buffer
      ByteBuf compositeBuffer() 分配一个CompositeByteBuf(复合)
      ByteBuf compositeBuffer(int maxNumComponents) 分配一个指定最大组件数量的CompositeByteBuf
      ByteBuf compositeHeapBuffer() 分配一个由heap buffer 组成的CompositeBytebuf
      ByteBuf compositeHeapBuffer(int maxNumComponents) 分配一个指定最大组件数量的由heap buffer 组成的CompositeByteBuf
      ByteBuf compositeDirectBuffer() 分配一个由direct buffer 组成的CompositeByteBuf
      ByteBuf compositeDirectBuffer(int maxNumComponents) 分配一个指定最大组件数量的由direct buffer组成的CompositeByteBuf
      boolean isDirectBufferPooled() 如果是direct buffer 并且是ByteBuf池的
      int calculateNewCapacity() 计算新容量,容量范围[minNewCapacity,maxNewCapacity],用于ByteBuf扩容时计算新容量
    • 实现类
      Class desc
      AbstractByteBufAllocator implements ByteBufAllocator ByteBufAllocator的抽象实现
      PooledByteBufAllocator extends AbstractByteBufAllocator 通过缓存池创建ByteBuf的工具类
      UnpooledByteBufAllocator extends AbstractByteBufAllocator 直接创建ByteBuf的工具类

结尾

本章简单介绍了ByteBuf的基本内容,以及基本的使用方法,后续内容具体讲解ByteBuf的创建、释放、缓存等流程。