目录
我们知道,网络数据的基本单位总是字节。Java NIO提供了ByteBuffer作为它的字节容器,但是这个类使用起来相当复杂复杂繁琐。
Netty的ByteBuffer替代品是ByteBuf,即解决了JDK API的局限性,又为网络应用程序的开发者提供了更好的API。
1.ByteBuf类
因为所有的网络通信都涉及到字节序列的移动,所以高效易用的数据结构明显是必不可少的。ByteBuf实现并超越了这些需求。
1.1 ByteBuf的工作原理
ByteBuf维护了两个不同的索引:一个用于读取,一个用于写入。当你从ByteBuf读取时,它的readerIndex将会被递增已经被读取的字节数。同样地,当你写入ByteBuf时,它的writerIndex也会被递增。
当读取字节直到readerIndex达到和writerIndex同样的值时,此时readerIndex到达“可以读取”数据的末尾,继续读取的话就会触发IndexOutOfBoundsException。
名称以read或者write开头的ByteBuf方法,将会推进其对应的索引,而名称以set或者get开头的操作则不会。ByteBuf的最大容量默认值是Integer.MAX_VALUE,当然我们也可以自定义容量,试图移动写索引(即writerIndex)超过这个值将会触发异常。
1.2 ByteBuf的使用模式
在使用Netty时,有几种常见的围绕ByteBuf而构建的使用模式:
-
堆缓冲区:将数据存储在JVM的堆空间中,GC可以及时的释放内存空间
-
直接缓冲区:避免在每次调用本地IO之前或之后,将缓冲区的内容复制到一个中间缓冲区,实现零拷贝。因为直接缓冲区驻留在垃圾回收扫描的堆区之外,因此需要手动管理内存。
-
复合缓冲区:可以创建多个不同的ByteBuf,然后提供一个这些ByteBuf的组合视图;复合缓冲区类似一个列表,我们可以动态的添加删除其中的ByteBuf。这是Netty中独有的功能,JDK中没有提供。
关于直接缓冲区和非直接缓冲区的理解可以参考文章:https://www.cnblogs.com/zxrxzw/p/11075951.html
ByteBuf提供了对字节的读写、查找、删除等操作,相关API参考官方API文档:https://netty.io/4.1/api/index.html
2.ByteBuf实例
2.1 ByteBufAllocator
为了降低分配和释放内存的开销,Netty通过ByteBufAllocator实现了(ByteBuf)池化,它可以用来分配我们所描述过的任意类型的ByteBuf。ByteBufAllocator常用的方法如下:
- buffer()\buffer(int initialCapacity)\buffer(int initialCapacity,int maxCapacity):返回一个基于堆或者直接内存存储的ByteBuf。
- heapBuffer()\heapBuffer(int initialCapacity)\heapBuffer(int initialCapacity,int maxCapacity):返回一个基于堆内存存储的ByteBuf。
- directBuffer()\directBuffer(int initialCapacity)\directBuffer(int initialCapacity,int maxCapacity):返回一个基于直接内存存储的ByteBuf。
- compositeBuffer()\compositeBuffer(int maxNumComponents)\compositeDirectBuffer()\compositeDirectBuffer(int maxNumComponents)\compositeHeapBuffer()\compositeHeapBuffer(int maxNumComponents):返回一个可以通过添加最大到指定数目的基于堆的或者直接内存存储的缓冲区来扩展的CompositeByteBuf。
Netty提供了两种ByteBufAllocator的实现:PooledByteBufAllocator和UnpooledByteBufAllocator。PooledByteBufAllocator池化了ByteBuf的实例以提高性能并最大限度的减少内存碎片,它采用了jemalloc一种已被大量现代操作系统所采用的高效方法来分配内存,是Netty的默认实现。UnpooledByteBufAllocator实现不池化ByteBuf实例,并且在每次它被调用时都会返回一个新的实例。
2.2 Unpooled缓冲区
Unpooled是Netty提供的一个简单的工具类,它提供了静态的辅助方法来创建未池化的ByteBuf实例。相关实现方法如下:
- buffer()\buffer(int initialCapacity)\buffer(int initialCapacity,int maxCapacity):返回一个基于堆内存存储的ByteBuf。
- directBuffer()\directBuffer(int initialCapacity)\directBuffer(int initialCapacity,int maxCapacity):返回一个未池化的基于直接内存存储的ByteBuf。
- wrappedBuffer():返回一个包装了给定数据的ByteBuf
- copiedBuffer():返回一个复制了给定数据的ByteBuf
2.3 ByteBufUtil类
ByteBufUtil提供了用于操作ByteBuf的静态的辅助方法,我们可以使用该工具类对ByteBuf进行处理。
3.小结
本章主要讨论了Netty的基于ByteBuf的数据容器。我们首先解释了ByteBuf相对于JDK所提供的实现的优势,以及相关API,最后指出了它们各自的最佳适用的特定用例。