Netty In Action - ByteBuf
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 | CompositeByteBuf messageBuf = Unpooled.compositeBuffer(); |
字节级操作
-
顺序访问索引
由于 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
5Channel channel = ...;
ByteBufAllocator allocator = channel.alloc();
ChannelHandlerContext ctx = ...;
ByteBufAllocator allocator2 = ctx.alloc();ByteBufAllocator 有两种实现:PooledByteBufAllocator 和 UnpooledByteBufAllocator。前者池化了ByteBuf的实例以提高性能并最大限度地减少内存碎片,而后者则没有堆实例进行池化。
-
Unpooled