Netty-ByteBuf

ByteBuf

由于NIO编程的复杂性,ByteBuffer也有其局限性,主要缺点如下:

  • ByteBuffer长度固定,一旦分配成功,容量无法动态扩展或者伸缩,当需要编码的POJO对象大于ByteBuffer容量,会出现越界
  • ByteBuffer只有一个标识位置的指针position,读写的时候需要不断地手工调用flip()和rewind()等,否则很容易导致程序失败
  • ByteBuffer的API功能不够齐全
ByteBuf的工作原理

ByteBuf依然是Byte数组的缓冲区,Netty中ByteBuf的实现有两种策略:

  • 参考JDK的ByteBuffer的实现,增加额外的功能
  • 聚合JDK的ByteBuffer,痛过Facade模式对其进行包装,减少自身代码量,降低实现成本
缓冲区的优化

ByteBuffer只有一个位置指针用于处理读写操作,而ByteBuf通过两个位置指针来协助缓冲区的读写操作,读操作用readerIndex,写操作用writeIndex。
readerIndex和writerIndex的取值一开始都是0,随着数的写入writerIndex会增加,读取数据会使readerIndex增加,但是它不会超过writerIndex。在读取之后,0~readerIndex的就被视为discard,调用discardReadBytes方法,可以释放着部分空间,相当于ByteBuffer的compact方法。readerIndex和writerIndex之间的数据是可读的,相等于ByteBuffer的position和limit之间的数据。writerIndex和capacity之间的空间是可写的,相当于ByteBuffer的limit和capacity之间的可用空间。
由于写操作不修改readerIndex指针,读操作不修改writerIndex指针,因此读写之间不再需要调整位置指针,极大地简化了缓冲区的读写操作
在这里插入图片描述
ps:0到readerIndex之间是已经读取过的缓冲区,可用调用discardReadBytes操作来重用这一部分,以节约内存,防止ByteBuf的动态扩张

动态扩展

通常情况下,当我们对ByteBuffer进行put操作,如果缓冲区剩余可写空间不够,就会发生BufferOverflowException。ByteBuf对write操作进行了封装,由ByteBuf的write操作辅助进行剩余空间的检验,如果可用缓冲区(也就是writerIndex之后的空间)不足,ByteBuf会自动进行动态扩展。

 public int writeBytes(InputStream in, int length) throws IOException {
   
        //判断剩余空间
        this.ensureWritable(length);
        int writtenBytes = this.setBytes(this.writerIndex, in, length);
        if (writtenBytes > 0) {
   
            this.writerIndex += writtenBytes;
        }

        return writtenBytes;
    }
public ByteBuf ensureWritable(int minWritableBytes) {
   
        if (minWritableBytes < 0) {
   
            throw new IllegalArgumentException(String.format("minWritableBytes: %d (expected: >= 0)", minWritableBytes));
        } else if (minWritableBytes <= this.writableBytes()) {
   
            return this;
        } else if (minWritableBytes > this.maxCapacity - this.writerIndex) {
   
            throw new IndexOutOfBoundsException(String.format("writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s", this.writerIndex, minWritableBytes, this.maxCapacity, this));
        } else {
   
            int newCapacity = this.calculateNewCapacity(this.writerIndex + minWritableBytes);
            this.capacity(newCapacity);
            return this;
        }
    }
常用操作

Discardable bytes
相比于其他Java对象,缓冲区的分配和释放是个耗时的操作,我们应该尽量重用它们。由于缓冲区的动态扩张需要进行字节数组的复制,是个耗时操作,为了提高性能,我们需要尽可能提升缓冲区的重用率。

假如缓冲区包含了N个整包消息,每个消息的长度为L,消息的可写字节数为R。当读取M个整包消息后,如果不对ByteBuf做压缩或者discardReadBytes操作,则可写的缓冲区长度依然为R。如果调用discardReadBytes操作,则可写的字节数会变为R = (R + M * L),之前已经读取的M个整包的空间会被重用。
ByteBuf的discardReadBytes操作之前:
在这里插入图片描述
操作之后:
在这里插入图片描述
Clear
Clear操作不会清空缓冲区内容本身,主要用来操作位置指针,如readerIndex和writerIndex,将它们还原为初始值。
Clear操作之前:
在这里插入图片描述
操作之后:
在这里插入图片描述
Mark和Rest
当对缓冲区进行读操作时,可能需要对之前的操作进行回滚。读操作不会改变缓冲区的内容,回滚操作主要是为了重新设置索引信息
对于JDK中的ByteBuffer,调用mark操作会将当前位置指针备份到mark变量中,当调用rest操作后,重新将指针的当前位置恢复为mark中的值。
Netty的ByteBuf也有类似的rest和mark接口,因为ByteBuf有读索引和写索引,对应此操作就有四种方法

  • markReaderIndex
  • restReaderIndex
  • markWriterIndex
  • restWriterIndex
 public ByteBuf markReaderIndex() {
   
        this.markedReaderIndex = this.readerIndex;
        return this;
    }

    public ByteBuf resetReaderIndex() {
   
        this.readerIndex(this.markedReaderIndex);
        return this;
    }

    public ByteBuf markWriterIndex() {
   
        this.markedWriterIndex = this.writerIndex;
        return this;
    }

    public ByteBuf resetWriterIndex() {
   
        this.writerIndex = this.markedWriterIndex;
        return this;
    }

Derived buffers
类似数据库的视图,ByteBuf提供多个接口用于创建某个ByteBuf的视图或者复制ByteBuf

  • duplicate:返回当前ByteBuf的复制对象,复制后返回的ByteBuf与操作的ByteBuf共享缓冲区的内容,但是维护自己独立的索引。当修改复制后的ByteBuf的内容之后,之前原ByteBuf的内容也随之改变。
  • copy:复制一个新的ByteBuf对象,它的内容和索引都是独立的,复制操作不修改原ByteBuf的读写索引
  • copy(int index,int length)
  • slice:返回当前ByteBuf的可读子缓冲区,起始位置从readerIndex到writerIndex,返回的ByteBuf与原ByteBuf共享内容,但是读写索引独立维护。该操作不修改原ByteBuf的索引位置
  • slice(int index,int length)

*如何将ByteBuf转换成ByteBuffer

  • ByteBuffer.nioBuffer():将当前ByteBuf可读的缓冲区转换成ByteBuffer,两者共享同一个缓冲区内容引用,对ByteBuffer的读写操作不会修改原ByteBuf的读写索引,返回后的ByteBuffer无法感知原ByteBuf的动
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值