Netty Review - 探索ByteBuf的内部机制

2023-11-24 09:48:16 浏览数 (2)


概念

ByteBuf是Netty中用于处理二进制数据的缓冲区

Netty的ByteBuf是一个可用于高效存储和操作字节数据的数据结构。与传统的ByteBuffer相比,ByteBuf提供了更灵活、更强大的API。

主要特性:

  • 可扩展性: ByteBuf支持动态扩展,可以自动扩展其容量以适应数据的增长。
  • 读写索引分离: 与ByteBuffer不同,ByteBuf有独立的读和写索引。这意味着可以读取和写入数据而无需手动切换模式。
  • 零拷贝: ByteBuf支持零拷贝操作,可以提高性能并降低内存复制的开销。
  • 池化支持: Netty提供了ByteBuf的池化支持,可帮助有效地重用内存以减少垃圾收集的压力。

ByteBuf VS Java NIO Buffer

ByteBuf则是Java NIO Buffer的新轮子,官方列出了一些ByteBuf的特性:

  • 需要的话,可以自定义buffer类型;
  • 通过组合buffer类型,可实现透明的zero-copy;
  • 提供动态的buffer类型,如StringBuffer一样,容量是按需扩展;
  • 无需调用flip()方法

ByteBuf实现类

ByteBuf提供了一些较为丰富的实现类,逻辑上主要分为两种:

  • HeapByteBuf
  • DirectByteBuf

实现机制则分为两种:

  • PooledByteBuf
  • UnpooledByteBuf

除了这些之外,Netty还实现了一些衍生ByteBuf(DerivedByteBuf),如:ReadOnlyByteBufDuplicatedByteBuf以及SlicedByteBuf

HeapByteBuf vs DirectByteBuf

HeapByteBufDirectByteBuf区别在于Buffer的管理方式:

  • HeapByteBuf由Heap管理,Heap是Java堆的意思,内部实现直接采用byte[] array;
  • DirectByteBuf使用是堆外内存,Direct应是采用Direct I/O之意,内部实现使用java.nio.DirectByteBuffer

PooledByteBuf vs UnpooledByteBuf

  • UnpooledByteBuf实现就是普通的ByteBuf了
  • PooledByteBuf是4.x之后的新特性

其他

  • DerivedByteBuf是ByteBuf衍生类,实现采用装饰器模式对原有的ByteBuf进行了一些封装。
  • ReadOnlyByteBuf是某个ByteBuf的只读引用;
  • DuplicatedByteBuf是某个ByteBuf对象的引用;
  • SlicedByteBuf是某个ByteBuf的部分内容。

ByteBuf的实现机制

Netty中的ByteBuf是一个强大的字节容器,用于处理字节数据。它的实现机制相当复杂,其主要特点如下

  • 内存分配: Netty的ByteBuf使用了一种称为池化的内存管理机制。这意味着它不是每次都直接分配新的内存,而是从预分配的内存池中获取。这有助于减少内存碎片化和提高性能。 4.x开发了Pooled Buffer,实现了一个高性能的buffer池,分配策略则是结合了buddy allocation和slab allocation的jemalloc变种,代码在io.netty.buffer.PoolArena
  • 引用计数: ByteBuf使用了引用计数机制来跟踪对它的引用。这种方式允许多个部分同时引用同一个ByteBuf,而不会导致内存泄漏。当引用计数降至零时,内存将被释放回池。
  • 可读写的索引: ByteBuf通过维护两个索引来实现读写操作,分别是读索引(readerIndex)和写索引(writerIndex)。这两个索引允许你从中读取数据或将数据写入,而不会相互影响。
  • 零拷贝: Netty的ByteBuf支持零拷贝的特性,这意味着在某些情况下,数据可以在不涉及实际数据复制的情况下传递给其他组件。这对于提高性能和降低资源消耗非常重要。 Zero-copy与传统意义的zero-copy不太一样。传统的zero-copy是IO传输过程中,数据无需中内核态到用户态、用户态到内核态的数据拷贝,减少拷贝次数。而Netty的zero-copy则是完全在用户态,或者说传输层的zero-copy机制,如下图。 由于协议传输过程中,通常会有拆包、合并包的过程,一般的做法就是System.arrayCopy了,但是Netty通过ByteBuf.slice以及Unpooled.wrappedBuffer等方法拆分、合并Buffer无需拷贝数据。 如何实现zero-copy的呢。slice实现就是创建一个SlicedByteBuf对象,将this对象,以及相应的数据指针传入即可,wrappedBuffer实现机制类似
  • Composite ByteBuf: Netty提供了CompositeByteBuf,它是一种特殊的ByteBuf,可以将多个ByteBuf组合成一个逻辑上的ByteBuf。这使得可以在不实际复制数据的情况下聚合多个缓冲区
代码语言:javascript复制
ByteBuf buffer1 = Unpooled.copiedBuffer("Hello, ".getBytes());
ByteBuf buffer2 = Unpooled.copiedBuffer("Netty!".getBytes());

CompositeByteBuf compositeBuffer = allocator.compositeBuffer();
compositeBuffer.addComponent(true, buffer1);
compositeBuffer.addComponent(true, buffer2);

// 使用compositeBuffer进行操作,它看起来像一个大的ByteBuf

CompositeByteBuf可以将多个ByteBuf组合成一个逻辑上的ByteBuf。

0 人点赞