ByteBuf和相关辅助类

1、ByteBuf功能说明

ByteBuffer的缺点

     1)ByteBuffer长度固定,一旦分配完成,它的容量不能动态扩展和收缩,当需要编码的POJO对象大于ByteBuf的容量时,会发生索引越界异常

   2)ByteBuffer只有一个标志位置的指针position,读写的时候需要手工平调用flip()和rewind()等,使用者必须小心谨慎地处理这些API,否则容易导致程序处理失败;

    3)ByteBuffer的API功能有限,一些高级和实用的特性它不支持,需要使用者自己编程实现。

 

2、ByteBuf的工作原理

    ByteBuf依然是一个Byte数组的缓冲区,它的基本功能应该与JDK的ByteBuffer一致,提供以下几类基本功能:

  • 7种Java基本类型、byte数组、ByteBuffer等的读写;
  • 缓冲区自身的copy和slice等;
  • 设置网络字节序;
  • 构造缓冲区实例;
  • 操作位置指针等方法。

ByteBuf通过两个位置指针来协助缓冲区的读写操作,读操作使用readerIndex,写操作使用writerIndex。

readerIndex和writerIndex的取值一开始都是0,随着数据的写入,writerIndex会增加,读取数据会使readerIndex增加,但是它不会超过writerIndex。在读取之后0~readerIndex就被视为discard的,调用discardReadBytes方法,可以释放这部分空间,它的作用类似ByteBuffer的compact方法。readerIndex和writerIndex之间的数据是可读取的,等价于ByteBuffer positoion和limit之间的数据。writerIndex和capacity之间的空间是可写的,等价于ByteBuffer limit和capacity之间的可用空间。

由于写操作不修改readerIndex指针,读操作不修改writerIndex指针,因此读写之间不需要调整位置指针,这极大地简化勒缓冲区的读写操作,避免勒由于一类或者不熟悉flip()操作导致的功能异常。

下面继续分析ByteBuf是如何实现动态扩展的。通常情况下,当我们对ByteBuffer进行put操作的时候,如果缓冲区剩余可写空间不够,就会发生BufferOverflowException异常。为了避免发生这个问题,通常在进行put操作的时候会对剩余可用空间进行校验。如果剩余空间不足,需要重新创建一个新的ByteBuffer,并将之前的ByteBuffer复制到新创建的ByteBuffer中,最后释放老的ByteBuffer,

为了防止ByteBuffer溢出,每进行一次put操作,都需要对可用空间进行校验,这导致代码冗余,稍有不慎,就可能引入其他问题。为了解决这个问题,ByteBuf对write操作进行勒封装,由ButeBuf的write操作负责进行剩余可用该空间的校验。如果可用缓冲区不足,ByteBuf会自动进行动态扩展。

当进行write操作时,会对需要write的字节进行校验。如果可写的字节数小于需要写入的字节数,并且需要写入的字节数小于可写的最大字节数。就对缓冲区进行动态扩展。由于NIO的Channel读写的参数都是ByteBuffer,因此,Netty的ByteBuf接口必须提供API,以方便地将ByteBuf转换成ByteBuffer,或者将ByteBuffer包装成ByteBuf。考虑到性能,应该尽量避免缓冲区的复制,内部实现的时候可以考虑聚合一个ByteBuffer的私有指针用来代表ByteBuffer。

3、ByteBuf的功能介绍

1)顺序读操作(read)

2)顺序写操作(write)

3)readerIndex和writerIndex

Netty提供勒两个指针变量用于支持顺序读取和写入操作:readerIndex用于读取标识读取索引,writerIndex用于标识写入索引。两个位置指针将ByteBuf缓冲区分割成三个区域。

调用ByteBuf的read操作时,从readerIndex处开始读取。readerIndex到writerIndex之间的空间为可读的字节缓冲区;从writerIndex到capacity之间为可写的直接缓冲区;0到readerIndex之间是已经读取过的缓冲区,可以调用discardReadBytes操作来重用这部分空间,以节约内存,防止ByteBuf的动态扩展。这在私有协议消息解码的时候非常有用,因为TCP底层可能粘包,几百个整包消息被TCP粘包后作为一个整包发送。这样,通过discardReadBytes操作可以重用之前已经解码过的缓冲区,从而防止接受缓冲区因为容量不足导致的扩张。

4)Discardable bytes

缓冲区的分配和释放是个耗时的操作,因此我们需要尽量重用它们。由于缓冲区的动态扩张需要进行自己数组的赋值,他是个耗时的操作,因此为了最大程度提升性能,往往需要尽最大努力提醒缓冲区的重用率。

5)readable bytes 和writerable bytes

可读空间段是数据时间存储的区域,以read或者skip开头的任何操作都将会从readerIndex开始读取或者跳过指定的数据,操作完成之后readerIndex增加了读取或者跳过的字节数长度。如果读取的字节数长度大于实际可读的字节数,则抛出IndexOutOfBoundsException。当新分配、包装或者负责一个新的ByteBuf对象时,它的readerIndex为0。

可写空间段是尚未被使用可以填充的空闲空间,任何以write开头的操作都会从writerIndex开始向空闲空间写入字节,操作完成之后writerIndex增加了写入的字节数长度。如果写入的字节数大于可写的字节数,则会抛出IndexOutOfBoundsException异常。新分配一个ByteBuf对象时,它的readerIndex为0。通过包装或者复制的方式创建一个新的ByteBuf对象时,它的writerIndex是ByteBuf的容量。

6)clear操作

7)mark和rest

当对缓冲区进行读操作时,由于某种原因,可能需要对之前的操作进行回滚。读操作并不会改变缓冲区的内容,回滚操作主要就是重新设置索引信息。

对于JDK的ByteBuffer,调用mark操作会将当前的位置指针备份到mark变量中,当调用rest操作之后,重新将指正的当前位置恢复为备份在mark中的值,

调用reset操作之后。

Netty的ByteBuf也有类似的rest和mark接口,因为ByteBuf有读索引和写索引,因此,它总共有4个相关的方法&#

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值