ByteBuf是什么
网络数据的基本单位总是字节,JAVANIO提供了ByteBuffer(缓冲区)作为它的字节容器,ByteBuf则是Netty提供的ByteBuffer替代品,使用起来更灵活简单。
所有的网络通信都涉及字节序列的移动,所以高效易用的数据结构明显是必不可少的,Netty的ByteBuf实现满足并超越的这些需求。
ByteBuf的API
主要通过ByteBuf、ByteBufHolder两个组件暴露
ByteBuf接口的优点:
- 可以被用户自定义的缓冲区类型扩展
- 通过内置的复合缓冲区类型实现了透明的零拷贝
- 容量可以按需增长(类似于JDK的StringBuilder),这个在JDK的ByteBuffer是不允许容量动态增长的,因为JDK创建ByteBuffer时必须指定容量,而ByteBuf默认最大可扩容量是Integer的最大值,容量默认是256。
- 在读和写两种模式之间切换不需要调用ByteBuffer的flip()方法
- 读和写使用了不同的索引
- 支持方法的链式调用
- 支持引用计数
- 支持池化(池化是通过ByteBufHolder实现的)
ByteBuf类的一些详细内容
如何工作
ByteBuf维护了两个不同的索引,一个用于读取,一个用于写入。当从ByteBuf读取时,它的readerIndex将会被递增已经被读取的字节数,同样如果是写入,它的writeIndex也会被递增。在JDK的只有一个position属性作为索引,读写需要切换模式。
名称以read或者write开头的ByteBuf方法将会推进其对应的索引,而名称以set或get开头的操作则不会。可以指定ByteBuf的容量(默认容量是Integer.MAX_VALUE),当writeIndex索引超过容量则会抛出异常。每次调用write方法都会判断当前ByteBuf是否需要扩容。ByteBuf的使用模式
- 堆缓冲区(在ByteBuffer中叫非直接缓冲区的一致)
将数据存储在JVM的堆空间中是最常用的ByteBuf模式,这种模式被称为支撑数组,它在没有使用池化的情况下提供快速的分配和释放。
ByteBuf.hasArray();方法判断是否有一个支撑数组,false即直接缓冲区,反之。
- 直接缓冲区
为了避免每次调用本地IO操作之前后将缓冲区的内容复制到一个中间缓冲区(或者从中间缓冲区把内容复制到应用缓冲区),NIO在JDK中引入的ByteBuffer类允许JVM实现通过本地调用来分配内存,用于对象创建的内存分配不会永远来自于堆中。
直接缓冲区的使用:
直接缓冲区的内容驻留在常规的会被垃圾回收的堆之外,如果数据在堆上分配的缓冲区中,那么套接字发送它之前,JVM将会在内部把你的缓冲区复制到一个直接缓冲区中,如此便多了一个步骤,所以直接缓冲区是一种理想的选择。但是相对于堆的缓冲区,它们的分配和释放较为昂贵,如果正在处理遗留代码,因为数据不是在堆上,又不得不进行一次复制。一般事先知道数据将会被作为数据来访问,应该更愿意使用堆内存。
- 复合缓冲区(接近ByteBuffer中的分散读取和聚集写入)
复合缓冲区为ByteBuf提供了一个聚合视图,在这里可以根据需要添加或者删除ByteBuf实例,这是JDK的ByteBuffer缺失的一个特性。
Netty通过一个ByteBuf子类CompositeByteBuf实现,它提供将多个缓冲区表示为单个缓冲区的虚拟表示。该子类实例可能包含前面两种模式的ByteBuf实例,如果其中只有一个ByteBuf实例,那么对于该子类实例的hasArray()方法调用的将是该ByteBuf的值,否则它将返回false。
/*** netty复合缓冲区的示例*/public static void demo() {CompositeByteBuf messageBuf = Unpooled.compositeBuffer();// 分配一个直接缓冲区ByteBuf headBuf = Unpooled.directBuffer();// 分配一个堆缓冲区ByteBuf footBuf = Unpooled.buffer();// 将建立两个缓冲区添加到复合缓冲区组件中messageBuf.addComponents(headBuf, footBuf);// 调用方法判断是否是支撑数组,==falseSystem.out.println(messageBuf.hasArray());// 删除索引位置为0的缓冲区messageBuf.removeComponent(0);// 调用方法判断是否是支撑数组,==true,因为只有一个实例时直接调用footBuf的hasArray()System.out.println(messageBuf.hasArray());for (ByteBuf byteBuf : messageBuf) {System.out.println(byteBuf);}}
@Overridepublic boolean hasArray() {switch (componentCount) {case 0:return true;case 1:return components[0].buf.hasArray();default:return false;}}
