美文网首页
netty源码分析(六) - ByteBuf - 1结构

netty源码分析(六) - ByteBuf - 1结构

作者: 进击的蚂蚁zzzliu | 来源:发表于2020-10-14 10:01 被阅读0次

概述

netty使用ByteBuf缓冲区来对读写字节进行操作,所谓缓冲区就是可以作为一个字节容器存储字节,等到一定大小之后再交给CPU去处理,提升效率;
ByteBuf提供了强大的API及体系,相比JDK的ByteBuffer通过position和limit来控制读写,每次切换都需要调用flip方法,ByteBuf通过writerIndex和readerIndex来简化控制,使用简单;

ByteBuf

ByteBuf维护了两个索引:readerIndex(读索引)和writerIndex(写索引)

 *      +-------------------+------------------+------------------+
 *      | discardable bytes |  readable bytes  |  writable bytes  |
 *      |                   |     (CONTENT)    |                  |
 *      +-------------------+------------------+------------------+
 *      |                   |                  |                  |
 *      0      <=      readerIndex   <=   writerIndex    <=    capacity
  • 通过readerIndex/writerIndex,ByteBuf被分成了三段,数据从左边读取,从右边写入
    1. discardable bytes:废弃区,数据已经被读取(废弃),随时可以清空(把readIndex移动到最左边)
    2. readable bytes:可读区,缓冲区中现存的实际数据,读取数据时readIndex向右移动
    3. writable bytes:可写区,目前是空的,写数据时writerIndex向右移动
  • ByteBuf接口中方法主要分为4类
    1. readxxx:读数据,readIndex向右移动
    2. writexxx:写数据,writerIndex向右移动
    3. setxxx:set数据,readIndex/writerIndex都不移动
    4. markxxx/resetxxx:标记/重置读或写标志位(先标记再进行读写异常回滚)

ByteBuf分类

ByteBuf.png
  • ByteBuf有三个维度进行分类:
    1. Pooled/Unpooled:是否使用对象池(默认使用),当对象释放后会归还给对象池,所以可循环使用。当需要大量且频繁创建缓冲区时,建议使用该类缓冲区
    2. Unsafe/非Unsafe:是否使用jdk底层Unsafe对象;
    • Unsafe可以直接通过内存地址+偏移量的方式对内存进行操作(UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index););
    • 非Unsafe直接通过数组下标操作数据(memory[index]);
    • Unsafe/非Unsafe是自动判别的,jdk底层有unsafe对象就分配unsafe否则才分配非unsafe对象
    1. Heap/Direct:Direct直接调用jdkAPI进行分配,不在堆上分配,不归jvm管理,需要手动释放,依赖jdk底层DirectByteBuffer;Heap的底层实现为JAVA堆内的字节数组

AbstractByteBuf

AbstractByteBuf是ByteBuf的骨架实现,包含各个读写索引,提供_get/_set方法让子类实现具体的字节操作逻辑;

//读索引
int readerIndex;
//写索引
int writerIndex;
//读标记
private int markedReaderIndex;
//写标记
private int markedWriterIndex;
//最大容量
private int maxCapacity;

以getInt()和readInt()为例,分析获取数据方法:

public int getInt(int index) {
    // 检查索引是否越界
    checkIndex(index, 4);   
    return _getInt(index);
}

public int readInt() {
    // 检查索引是否越界
    checkReadableBytes0(4);
    int v = _getInt(readerIndex);
    // 读索引右移
    readerIndex += 4;   
    return v;
}

protected abstract int _getInt(int index);  

可见readInt与getInt都是模板方法的模式,先对索引进行检查,然后将实际操作交给子类负责具体实现的_getInt()方法;两者的区别在于是否自动维护读索引,readInt将增加读索引,getInt则不会对索引产生任何影响。

以setInt()和writeInt()为例,分析写入数据方法

public ByteBuf setInt(int index, int value) {
    checkIndex(index, 4);
    _setInt(index, value);
    return this;
}

public ByteBuf writeInt(int value) {
    ensureAccessible();
    ensureWritable0(4);
    _setInt(writerIndex, value);
    writerIndex += 4;
    return this;
}

protected abstract void _setInt(int index, int value);

ByteBufAllocator(内存分配器)分类

ByteBufAllocator.png
  • ByteBufAllocator:最顶底层的抽象,负责分配所有类型的内存,包括:buffer/ioBuffer/heapBuffer/directBuffer/compositeBuffer(可以把多个ByteBuf合并成一个,很少使用)
  • AbstractByteBufAllocator:实现了ByteBufAllocator大部分的功能,模板方法中提供了newHeapBuffer/newDirectBuffer两个抽象方法供子类扩展分配不同种类的ByteBuf
  • UnpooledByteBufAllocator:分配不使用内存池的ByteBuf,通过newHeapBuffer方法分配UnpooledUnsafeHeapByteBuf/UnpooledHeapByteBuf;通过newDirectBuffer分配UnpooledUnsafeDirectByteBuf/UnpooledDirectByteBuf;
  • PooledByteBufAllocator:分配使用内存池的ByteBuf,通过newHeapBuffer方法分配PooledUnsafeHeapByteBuf/PooledHeapByteBuf;通过newDirectBuffer分配PooledUnsafeDirectByteBuf/PooledDirectByteBuf;
  • 注意:ByteBuf分类的三个维度:
    1. Pooled/Unpooled:通过不同的子类UnpooledByteBufAllocator/PooledByteBufAllocator区分
    2. Heap/Direct:通过不同分配方法newHeapBuffer/newDirectBuffer区分
    3. Unsafe/非Unsafe:具体分配时通过当前平台是否有Unsafe区分(用户不需要关系)PlatformDependent.hasUnsafe();

ByteBuf结构分析到此结束,下一节详细讲解各中类型ByteBuf如何分配及重点方法实现

相关文章

网友评论

      本文标题:netty源码分析(六) - ByteBuf - 1结构

      本文链接:https://www.haomeiwen.com/subject/sudvpktx.html