本文共 4008 字,大约阅读时间需要 13 分钟。
网络数据的基本单位总是字节。Java NIO 提供了 ByteBuffer 作为它 的字节容器,但是这个类使用起来过于复杂,而且也有些繁琐。Netty 的 ByteBuffer 替代品是 ByteBuf,一个强大的实现,既解决了 JDK API 的局限性, 又为网络应用程序的开发者提供了更好的 API。
针对I/O操作的读与写,分别维护对应的readerIndex与writerIndex两个指针,两者是互补影响的,读操作会使得readerIndex移动,写操作会使得writerIndex移动,整体的容量大小由capacity来表示,初始状态readerIndex与writerIndex都是0,而capacity是申请的容器大小。
+-------------------+------------------+------------------+ | discardable bytes | readable bytes | writable bytes | | | (CONTENT) | | +-------------------+------------------+------------------+ | | | | 0 <= readerIndex <= writerIndex <= capacity
该图能够反映互相之间的关系:
Readable bytes:这部分是存储的实际内容,可以用来被读取,被读取之后readerIndex将会相应地增加
// Iterates the readable bytes of a buffer.{@link ByteBuf} buffer = ...;while (buffer.isReadable()) { System.out.println(buffer.readByte());}
Writable bytes:这部分是需要被填充的部分,任何与写相关的操作都会使writerIndex相应地增加
// Fills the writable bytes of a buffer with random integers.ByteBuf buffer = ...;while (buffer.maxWritableBytes() >= 4) { buffer.writeInt(random.nextInt());}
Discardable bytes:这部分包含已经被读操作读取过的内容,能够通过调用discardReadBytes()方法使这部分被抛弃,也就会达到以下状态:
+------------------+--------------------------------------+ | readable bytes | writable bytes (got more space) | +------------------+--------------------------------------+ | | |readerIndex (0) <= writerIndex (decreased) <= capacity
这种抛弃操作可能会使Writable bytes这部分被移动,但是更推荐使用以下clear操作,仅仅将readerIndex与writerIndex进行处理
clear操作:将readerIndex和writerIndex都设置成0,并不会实际清楚ByteBuf中的内容,调用该方法之后将会达到以下状态:
+---------------------------------------------------------+ | writable bytes (got more space) | +---------------------------------------------------------+ | | 0 = readerIndex = writerIndex <= capacity
Heap buffer
堆缓冲区是常用的一种类型,ByteBuf将数据存储到JVM的堆空间中,并且将实际的数据存放到byte array中来实现Direct buffer
直接缓冲区,在堆外直接分配内存空间,直接缓冲区并不会占用堆的容量空间,因为它是由操作系统在本地内存进行的数据分配直接缓冲区并不支持通过字节数组的方式来访问数据。对于后端业务的消息编解码来说,推荐使用HeapByteBuf;对于I/O通信线程在读写缓冲区时,推荐使用DirectByteBuf
为了降低分配和释放内存的开销,Netty 通过 interface ByteBufAllocator 实现了 (ByteBuf 的)池化,它可以用来分配我们所描述过的任意类型的 ByteBuf 实例。可以通过 Channel(每个都可以有一个不同的 ByteBufAllocator 实例)或者绑定到 ChannelHandler 的 ChannelHandlerContext 获取一个到 ByteBufAllocator 的引用。Netty提供了两种ByteBufAllocator的实现:PooledByteBufAllocator和UnpooledByteBufAllocator。前者池化了ByteBuf的实例以提高性能并最大限度地减少内存碎片。后者的实现不池化ByteBuf实例,并且在每次它被调用时都会返回一个新的实例。虽然Netty默认使用了PooledByteBufAllocator,但这可以很容易地通过ChannelConfig API或者在引导你的应用程序时指定一个不同的分配器来更改
可能某些情况下,你未能获取一个到 ByteBufAllocator 的引用。对于这种情况,Netty 提 供了一个简单的称为 Unpooled 的工具类,它提供了静态的辅助方法来创建未池化的 ByteBuf 实例。Unpooled 类还使得 ByteBuf 同样可用于那些并不需要 Netty 的其他组件的非网络项目, 使得其能得益于高性能的可扩展的缓冲区 API。
转载地址:http://shrgi.baihongyu.com/