Netty In Action 读书笔记 - 第五章 Buffer

本章包括:
1、ByteBuf
2、ByteBufHolder
3、ByteBufAllocator
4、对这些接口进行分配和执行操作

Netty提供了强大的Buffer实现,用来表示一个字节序列,并帮助我们操作原始的字节流或自定义的POJO。

Buffer API
两个接口:
-ByteBuf
-ByteBufHolder
Netty使用reference-counting得知何时释放一个Buf和它申请的系统资源是安全的。这使得Netty可以使用池化和其它技巧来加速应用,并将内存使用维持在一个稳定的级别。在开发Netty应用时,最好在处理完数据之后,尽快释放被池化的资源。

Netty的Buffer API还有如下好处:
-如果需要,可以自定义buffer类型;
-透明零拷贝已由内建的组合buffer类型实现;
-可按需扩容,就像StringBuffer一样;
-无需调用flip()方法来切换读/写模式;
-独立的读、写指针;
-方法链;
-引用计数;
-池化;

ByteBuf - Byte数据容器
ByteBuf是一个数据容器,允许我们以非常有效的方式向其添加或从中获取字节数组。为了简化操作,它使用两个指针:一个用于读操作,一个用于写操作。这允许我们可以顺序的从中读取数据,并且“跳回”重读,需要做的仅仅是调整读指针的位置,再执行读操作即可。

工作机制:
在写入数据时,它的writerIndex增加了写入字符长度的值;在读取数据后,它的readerIndex增加了读取字符长度的值。我们可以一直读取数据,直到readerIndex等于writerIndex。此时ByteBuf变为不可读,下一次读请求将触发IndexOutofBoundException。


不同的ByteBuf类型:
HEAP BUFFERS:
最常用的是使用JVM的堆内存存储数据的ByteBuf。底层是通过数组来支持的。在不使用池的情况下,内存分配和回收都比较快。

DIRECT BUFFERS:
直接分配内存,而不是在JVM堆上。在通过socket传输数据时是一种优化(实际上JVM也是拷贝到DIRECT BUFFER,再发送给socket传输的)。不好的是,直接分配内存及回收的代价比较昂贵(Full GC)。这也是为什么Netty支持池化。

COMPOSITE BUFFERS:
合并不同类型的ByteBuf实例,并提供统一视图。长处是可以在运行时添加或删除ByteBuf实例,就像是一个List。



ByteBuf的字符操作
随机访问 - “0基索引”,第一个比特的索引为0,最后一个比特的索引为容量-1;
注意,通过索引传递访问数据不会改变readerIndex和writerIndex。需要的话,可以手动调用readerIndex(index)和writerIndex(index)来进行更新。

顺序访问 - 读下标、写下标分别支持读操作、写操作。


1、持有之前已经被读取过的、可以被丢弃的字节数据的段;
2、持有尚未被读取的、实际可读的字节数据的段;
3、持有buffer剩余空间的,可以写入更多字节数据的段;

Discardable bytes - 该段包含了已经被读取过的字节数据,因此可以被丢弃掉。开始时这个段的长度为0,当读操作执行时,这个值会一直增加到writerIndex。只有“read”操作会引起readerIndex变化,“get”操作不会。调用discardReadBytes()会丢弃已读字节数据,并回收没用的空间。

调用 discardReadBytes()会引起内存拷贝,该操作不是没有代价的并且可能会影响性能。仅在需要尽快释放内存时才考虑调用。

Readable bytes
如果读操作超出readerIndex,会抛出IndexOutofBoundException。初始化的值为0。

Writable bytes
如果没有足够的可写bytes,会抛出IndexOutofBoundsException。初始化的值为0。

清理buffer indexes
clear()方法会将readerIndex和writerIndex都置为0,但是并不清理buffer的内容。与 discardReadBytes()方法相比,clear()方法代价更小,因为它仅调整指针的位置,而不需要任何内存拷贝。

搜索操作
各种indexOf()方法帮助我们定位一个满足特殊规则的值。ByteBufProcessor实现类可以帮助搜索。

标记和重置
readerIndex(index)和writerIndex(index)来进行更新下标位置,如果index值是非法的位置,则会抛出IndexOutOfBoundExceptoin。

Derived buffers
调用duplicate(), slice(), slice(int, int), readOnly()或order(ByteOrder)。衍生的buffer有独立的readerIndex、writerIndex和marker indexes,但它跟NIO ByteBuffer一样,共享其它内部数据表现。由于共享内部数据表现,slice是更廉价的,也是更被推荐使用的方式。如果需要现有buffer的全新拷贝(需要内存拷贝),使用copy()或copy(int, int)代替。
尽可能使用slice,在需要时使用copy。

读/写操作
参考API说明。

其它有用操作
isReadable()、isWritable()、readableBytes()、writableBytes()、capacity()、maxCapacity()、hasArray()、array()等。

ByteBufHolder
data() - 返回持有数据的ByteBuf对象;
copy() - 返回ByteBufHolder的一个拷贝,不共享数据(数据也是拷贝的);

Netty的buffer工具类
ByteBufAllocator - Allocate ByteBuf when needed 
ByteBufAllocator可从Channel或ChannelHandlerContext获取,它的一些常用方法,见API说明。
Netty有两种 ByteBufAllocator实现,一种缓存了ByteBuf实例来最小化内存分配/回收的代价,并维持内存碎片在最低水平。另外一种则每次都返回新的ByteBuf实例。Netty默认使用PooledByteBufAllocator实现,但可以通过ChannelConfig轻易的进行调整。

Unpooled - Buffer creation made easy
Unpooled工具类,提供了静态方法创建未被缓存的ByteBuf实例。可以在Netty之外使用ByteBuf。

ByteBufUtil - Small but useful
也是有用的工具类,独立于Unpooled类是因为:它的方法都是一般化的,并且不依赖与ByteBuf是否被缓存。
hexdump()方法:将ByteBuf的内容以hex表达形式打印出来,可用于debug。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值