概述
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被分成了三段,数据从左边读取,从右边写入
- discardable bytes:废弃区,数据已经被读取(废弃),随时可以清空(把readIndex移动到最左边)
- readable bytes:可读区,缓冲区中现存的实际数据,读取数据时readIndex向右移动
- writable bytes:可写区,目前是空的,写数据时writerIndex向右移动
- ByteBuf接口中方法主要分为4类
- readxxx:读数据,readIndex向右移动
- writexxx:写数据,writerIndex向右移动
- setxxx:set数据,readIndex/writerIndex都不移动
- markxxx/resetxxx:标记/重置读或写标志位(先标记再进行读写异常回滚)
ByteBuf分类
ByteBuf.png- ByteBuf有三个维度进行分类:
- Pooled/Unpooled:是否使用对象池(默认使用),当对象释放后会归还给对象池,所以可循环使用。当需要大量且频繁创建缓冲区时,建议使用该类缓冲区
- Unsafe/非Unsafe:是否使用jdk底层Unsafe对象;
- Unsafe可以直接通过内存地址+偏移量的方式对内存进行操作(
UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index);
); - 非Unsafe直接通过数组下标操作数据(
memory[index]
); - Unsafe/非Unsafe是自动判别的,jdk底层有unsafe对象就分配unsafe否则才分配非unsafe对象
- 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分类的三个维度:
- Pooled/Unpooled:通过不同的子类UnpooledByteBufAllocator/PooledByteBufAllocator区分
- Heap/Direct:通过不同分配方法newHeapBuffer/newDirectBuffer区分
- Unsafe/非Unsafe:具体分配时通过当前平台是否有Unsafe区分(用户不需要关系)
PlatformDependent.hasUnsafe()
;
ByteBuf结构分析到此结束,下一节详细讲解各中类型ByteBuf如何分配及重点方法实现
网友评论