ByteBuf API

特点

  • 它可以被用户自定义的缓冲区类型扩展;
  • 通过内置的复合缓冲区类型实现了透明的零拷贝;
  • 容量可以按需增长(类似于 JDK 的 StringBuilder);
  • 在读和写这两种模式之间切换不需要调用 ByteBuffer 的 flip()方法;
  • 读和写使用了不同的索引;
  • 支持方法的链式调用;
  • 支持引用
  • 支持池化

工作方式

ByteBuf 维护了两个不同的索引:一个用于读取,一个用于写入。当从 ByteBuf 读取时, 它的 readerIndex 将会被递增已经被读取的字节数。同理,当写入 ByteBuf 时,它的 writerIndex 也会被递增。

名称以 read 或者 write 开头的 ByteBuf 方法,将会推进其对应的索引,而名称以 set 或者 get 开头的操作则不会。

使用模式

  • 堆缓冲区

    这个模式下,数据会存储在 JVM 堆空间中,这种模式也称作“支持数组”(backing array)它能在米有使用池化的情况下提供快速的分配和释放。

    适合于有遗留的数据需要处理的情况。

  • 直接缓冲区

    如同 JDK 的 ByteBuffer 一样,数据存储在常规的会被垃圾回收的堆之外。但这有一个缺点,就是如果需要对数据进行操作,则需要拷贝一份到 JVM 内存中。

  • 复合缓冲区

    这是为多个 ByteBuf 提供的聚合试图,可以根据需要添加或者删除 ByteBuf 实例。

    Netty 通过一个 ByteBuf 子类——CompositeByteBuf——实现了这个模式,它提供了一个将多个缓冲区表示为单个合并缓冲区的虚拟表示。

1
2
3
4
5
6
7
8
CompositeByteBuf messageBuf = Unpooled.compositeBuffer();
ByteBuf headerBuf = ...;
ByteBuf bodyBuf = ...;
messageBuf.addComponents(headerBuf, bodyBuf);
messageBuf.removeComponent(0);
for (ByteBuf buf : messageBuf) {
System.out.println(buf.toString());
}

字节级操作

  • 顺序访问索引

    由于 JDK 的 ByteBuffer 只有一个索引,所以即使 ByteBuf 同时具有读索引和写索引,仍然需要调用 flip() 方法 再读写模式之间切换

  • 可丢弃字节

    ByteBuf 的可读字节分段存储了实际数据。新分配的、包装的或者复制的缓冲区的默认的 readerIndex 值为 0。任何名称以 read 或者 skip 开头的操作都将检索或者跳过位于当前 readerIndex 的数据,并且将它增加已读字节数。

  • 可写字节

    可写字节分段是指一个拥有未定义内容的、写入就绪的内存区域。新分配的缓冲区的 writerIndex 的默认值为 0。任何名称以 write 开头的操作都将从当前的 writerIndex 处 开始写数据,并将它增加已经写入的字节数。如果写操作的目标也是 ByteBuf,并且没有指定 源索引的值,则源缓冲区的 readerIndex 也同样会被增加相同的大小。

  • 索引管理

    通过调用 markReaderIndex()markWriterIndex()resetWriterIndex()resetReaderIndex()来标记和重置 ByteBuf 的 readerIndex 和 writerIndex。

    也可以通过调用 readerIndex(int)或者 writerIndex(int)来将索引移动到指定位置。

    可以通过调用 clear()方法来将 readerIndex 和 writerIndex 都设置为 0。但并不会清除内存中的内容。

  • 查找操作

    indexOf()
    ByteProcessor

  • 派生缓冲区

  • 读/写操作

    • get()和 set()操作,从给定的索引开始,并且保持索引不变;
    • read()和 write()操作,从给定的索引开始,并且会根据已经访问过的字节数对索引进行调整。

  • get()操作
名称 描述
getBoolean(int) 返回给定索引处的 Boolean 值
getByte(int)
getUnsignedByte(int) 将给定索引处的无符号字节值作为 short 返回
getMedium(int) 返回给定索引处的 24 位的中等 int 值
getUnsignedMedium(int) 返回给定索引处的无符号的 24 位的中等 int 值
getInt(int)
getUnsignedInt(int) 将给定索引处的无符号 int 值作为 long 返回
getLong(int)
getShort(int)
getUnsignedShort(int) 将给定索引处的无符号 short 值作为 int 返回
getBytes(int, …) 将该缓冲区中从给定索引开始的数据传送到指定的目的地
  • set()操作
名称 描述
setBoolean(int, boolean) 设定给定索引处的 Boolean 值
setByte(int index, int value)
setMedium(int index, int value) 设定给定索引处的 24 位的中等 int 值
setInt(int index, int value)
setLong(int index, long value)
setShort(int index, int value)
  • read()操作
名称 描述
readBoolean()
readByte()
readUnsignedByte()
readMedium()
readUnsignedMedium()
readInt()
readUnsignedInt()
readLong()
readShort()
readUnsignedShort()
readBytes(ByteBuf| byte[] destination,int dstIndex [,intlength])
  • write()操作
名称 描述
writeBoolean(boolean)
writeByte(int)
writeMedium(int)
writeInt(int)
writeLong(long)
writeShort(int)
writeBytes(source ByteBuf |byte[] [,int srcIndex ,int length])
  • 其他有用的操作
名称 描述
isReadable() 如果至少有一个字节可供读取,则返回 true
isWritable() 如果至少有一个字节可被写入,则返回 true
readableBytes() 返回可被读取的字节数
writableBytes() 返回可被写入的字节数
capacity() 返回 ByteBuf 可容纳的字节数。在此之后,它会尝试再次扩展直 到达到 maxCapacity()
maxCapacity() 返回 ByteBuf 可以容纳的最大字节数
hasArray() 如果 ByteBuf 由一个字节数组支撑,则返回 true
array() 如果 ByteBuf 由一个字节数组支撑则返回该数组;否则,它将抛出一个 UnsupportedOperationException 异常

ByteBuf 分配 (池化&非池化)

  • ByteBufAllocator
    为了降低分配和释放内存的开销,Netty 通过 interface ByteBufAllocator 实现了 (ByteBuf 的)池化,它可以用来分配我们所描述过的任意类型的 ByteBuf 实例。

    可以通过 Channel(每个都可以有一个不同的 ByteBufAllocator 实例)或者绑定到 ChannelHandler 的 ChannelHandlerContext 获取一个到 ByteBufAllocator 的引用。

    1
    2
    3
    4
    5
    Channel channel = ...;
    ByteBufAllocator allocator = channel.alloc();

    ChannelHandlerContext ctx = ...;
    ByteBufAllocator allocator2 = ctx.alloc();

    ByteBufAllocator 有两种实现:PooledByteBufAllocator 和 UnpooledByteBufAllocator。前者池化了ByteBuf的实例以提高性能并最大限度地减少内存碎片,而后者则没有堆实例进行池化。

  • Unpooled