前言
在网络传输过程中,字节是最基本也是最小的单元。JAVA NIO有提供一个ByteBuffer容器去装载这些数据,但是用起来会有点复杂,经常要在读写间进行切换以及不支持动态扩展等等。而netty为我们提供了一个ByteBuf组件,功能是很强大的,本文主要对ByteBuf进行一些讲解,中间会穿插着和ByteBuffer进行对比。
优势
ByteBuf与ByteBuffer的相比的优势:
- 读和写用不同的索引。
- 读和写可以随意的切换,不需要调用flip()方法。
- 容量能够被动态扩展,和StringBuilder一样。
- 用其内置的复合缓冲区可实现透明的零拷贝。
- 支持方法链。
- 支持引用计数。count == 0,release。
- 支持池。
下面将会对每一种优势进行详细的解读。
读写索引
讯享网
可以根据下面简单的代码自行测试一下:
讯享网
可以根据下面简单的代码自行测试一下:
讯享网
动态扩展
ByteBuffer是不支持动态扩展的,给定一个具体的capacity,一旦put进去的数据超过其容量,就会抛出异常,而ByteBuf完美的解决了这一问题,支持动态扩展其容量。
零拷贝
netty提供了CompositeByteBuf类实现零拷贝。大多数情况下,在进行网络数据传输时我们会将消息分为消息头head和消息体body,甚至还会有其他部分,这里我们简单的分为两部分来进行探讨:

以前的做法
这样为了得到完整的消息体相当于对内存进行了多余的两次拷贝,造成了很大的资源的浪费。
netty提供的方法
讯享网
这里通过CompositeByteBuf 对象将headerBuf 与bodyBuf组合到了一起,也得到了完整的消息体,但是并未进行内存上的拷贝。可以注意下我在上面代码段中进行的方法的调用,得出来的结果是:指向的还是原来分配的空间地址,也就证明了零拷贝的观点。
支持引用计数
看一段简单的代码段:
这里我感觉就是AQS差不多的概念,retain和lock类似,release和unlock类似,内部维护一个计数器,计数器到0的时候就表示已经释放掉了。往一个已经被release掉的buffer中去写数据,会抛出异常。
在Netty in Action一书中对其的介绍是:
引用计数器实现的原理并不复杂,仅仅只是涉及到一个指定对象的活动引用,对象被初始化后引用计数值为1。只要引用计数大于0,这个对象就不会被释放,当引用计数减到为0时,这个实例就会被释放,被释放的对象不应该再被使用。

支持池
Netty对ByteBuf的分配提供了池支持,具体的类是。用这个分配器去分配ByteBuf可以提升性能以及减少内存碎片。Netty中默认用当做ByteBuf的分配器。对象可以从Channel中或者绑定了Channel的ChannelHandlerContext中去获取到。
讯享网
API介绍(介绍容易混淆的几个)
创建ByteBuf
读写数据(readByte writeByte)
讯享网
进行readByte和writeByte方法的调用时会改变readIndex和writeIndex的值,而调用set和get方法时不会改变readIndex和writeIndex的值。上面的测试案例中打印的writeIndex和readIndex均为1,并未在调用set和get方法后被改变。
discardReadBytes方法
从上面的图中可以观察到,调用discardReadBytes方法后,readIndex置为0,writeIndex也往前移动了Discardable bytes长度的距离,扩大了可写区域。但是这种做法会严重影响效率,它进行了大量的拷贝工作。如果要进行数据的清除操作,建议使用clear方法。调用clear()方法将会将readIndex和writeIndex同时置为0,不会进行内存的拷贝工作,同时要注意,clear方法不会清除内存中的内容,只是改变了索引位置而已。
Derived buffers
这里介绍三个方法(浅拷贝):
上面的所有测试代码均可以在我的github中获取(netty中的buffer模块)。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/193042.html