Netty-ByteBuf

作者 chauncy 日期 2020-05-20
Netty-ByteBuf

回顾一下之前使用Netty 进行开发的一个百万物联网设备网关,其中我觉得里面最好用的就是Netty 的ByteBuf,从源码整体的设计回顾一下

ByteBuf 分类

在Netty中他的ByteBuf 一共有 有4 种类型吧
从他所在的内存区域来划分:

  • Heap java 堆内存
  • Off Heap java 的堆外(跟DirectBuffer 一样)
    从是否进行池话来看:
  • Pooled
  • Unpooled
    所用总共就有4类
  • Pooled Heap
  • Unpooled Heap
  • Pooled off heap
  • Unpoop off heap

整个层次结构

heap-pool图

ReferenceCounted 用于引用计数,就是专门针对pool 的
ByteBuf 是一个操作的抽象,定义了read write 的一些一些方法
AbstractReferenceCountedByteBuf 就是对上面一些封装,资源的回收的
底层就有 PooledByteBuf UnpooledHeapByteBuf UnpooledDirectByteBuf

ByteBufAllocator 是一个专门用于allocat 各种buffer,可以理解为一个抽象工厂
对于具体的抽象工厂他有UnpooledByteBufAllocator PooledByteBufAllocator 的实现
其中PooledByteBufAllocator 里面含有一个PoolArena ,他是每一个线程都有自己独立的PoolArena ,这样的设计也降低了线程资源的并发需要一些同步带来的复杂设计,已经性能降低

PoolArena 里面包含了PooledByteBuf ,而到他们分别的子类PooledHeapByteBuf PooledDirectByteBuf 和 HeapArena DirectArena 具体去实现create 操作
这里也是用了 桥接的设计模式在里面 PooledByteBuf PoolArena 进行一个连接
PooledByteBufAllocator 关联了PoolArena 从而 将factory 和具体的生成的product 进行一个结合

ByteBuf 的具体实现

一般当我们数据到来了,io 线程指向decode里面可以是使用的是Unpooled.headBuffer() ,因为我们这个需要在业务里面进行不断的对他进行操作
如果是进行io 的话,使用Direct buf 这样减少io 的copy
这仅仅以Unpooled.headBuffer 例子
它的里面其实一个 byte[] array; 数组用于存放真正的数据
当我们对他进行具体的read 和write 的时候其实是操作的是readIndex writeIndex

基类 AbstractByteBuf

public abstract class AbstractByteBuf extends ByteBuf {
int readerIndex;
int writerIndex;
private int markedReaderIndex;
private int markedWriterIndex;
private int maxCapacity;
}

看一下他的read short

@Override
public short readShort() {
checkReadableBytes0(2);
short v = _getShort(readerIndex);
readerIndex += 2;
return v;
}

因为short 占用2 个字节,所以他会 readIndex+=2,读操作就是对readIndex 进行

同理写是对writeIndex

public ByteBuf writeShort(int value) {
ensureWritable0(2);
_setShort(writerIndex, value);
writerIndex += 2;
return this;
}

如下图
bytebuf read write

当read index 超过 wriete index 会有一个 数组越界的异常
capacity 是当前的容量,这个容量是扩容的,最大容量是int 的最大值

ByteBuf 与ByteBuffer 对比

  • 首先 ByteBuf 他是读写分离的,(read index write index 是分开的)
  • ByteBuffer 他是金有个 postion(当前的的位置) limit (如果是可读,表示当前最大的读的位置) capacity 需要读写切换的时候需要调用flip 函数,进行转换,操作很麻烦且容易出错,若两次连续调用则就没有数据可读

  • ByteBuffer 里面是用的是一个final byte [] 不能进行扩容,ByteBuf 可以进行扩容,并且扩容的时候为多线程使用了自旋+cas 操作保证了thread safe

  • ByteBuf 还可以进行pool,使用reference count