netty权威指南学习笔记
netty实战
java.nio.Buffer
javaNIO对于7种基本数据类型(Boolean除外)都有自己的缓冲区实现,对于NIO而言我们最常使用的是ByteBuffer
从功能上看,ByteBuffer完全可以满足NIO编程的需要,但是ByteBuffer也有它自身的局限性:
- ByteBuffer长度固定,一单分配完成,它的容量无法自动扩展和收缩
- ByteBuffer只有一个位置表示position,读写操作时需要手工调用flip()和rewind()方法等
public final Buffer flip() {
limit = position; //限制设置为当前位置
position = 0;//,然后将位置设置为 0
mark = -1; //丢弃标记。
return this;
}
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
- ByteBuffer的API功能有限
为了弥补这些不足,Netty提供了自己的ByteBuffer实现ByteBuf
ByteBuf工作说明
ByteBuf API 的优点:
- 它可以被用户自定义的缓冲区类型扩展;
- 通过内置的复合缓冲区类型实现了透明的零拷贝;
- 容量可以按需增长(类似于 JDK 的StringBuilder);
- 在读和写这两种模式之间切换不需要调用 ByteBuffer 的 flip()方法;
- 读和写使用了不同的索引;
- 支持引用计数;
- 支持池化;
ByteBuf的工作原理
ByteBuf依然是一个Byte数组缓冲区,它的基本功能与nio的ByteBuffer一致,二者最大的不同有如下。
ByteBuffer只有一个指针用于读写操作,每次读写都需要调用flip(),clear()等方法,否则功能将会出错,
,而ByteBuf通过两个指针来协助缓冲区读写操作,读操作readerIndex,写操作用writerIndex.二者一开始都是0,随着数据的写入,writerIndex会增加,读取数据readerIndex增加,但是它不会超过writerIndex.
ByteBuf的功能介绍
- 顺序读操作(read):类似于ByteBuffer的get操作,readXXX主要方法:
//读取一个32位的整数,readerIndex增加4
public abstract int readInt();
//读取一个64位的整数,readerIndex增加8
public abstract long readLong();
//读取一个2字节UTF-16 character
public abstract char readChar();
/*
将当前ByteBuf中的数据读取到新的ByteBuf中去,读取长度为length;
返回的ByteBuf的readerIndex=0,writerIndex=length
*/
public abstract ByteBuf readBytes(int length);
//返回当前ByteBuf创建的子区域,子区域与原ByteBuf共享缓冲区,但是独立维护readerIndex和writerIndex
public abstract ByteBuf readSlice(int length);
/*
将当前ByteBuf的数据读取到目标ByteBuf,直到目标ByteBuf没有剩余空间可写。
若目标ByteBuf的可写空间 > 当前ByteBuf的可读空间,则抛出IndexOutOfBoundsExeption
*/
public abstract ByteBuf readBytes(ByteBuffer dst);
- 顺序写操作(write):类似于ByteBuffer的put,writeXXX方法(略)
- Mark和Rest:ByteBuf有读索引和写索引所以,它共有2*2个方法
- markReaderIndex
- markWriterIndex
- resetReaderIndex
- resetWriterIndex
- 查找操作:从ByteBuf查找某个字符
//从fromIndex到toIndex区间内,第一次出现value的位置索引
public abstract int indexOf(int fromIndex, int toIndex, byte value);
//从readerIndex到writerIndex区间内,第一次出现value的位置索引
public abstract int bytesBefore(byte value);
//从readerIndex到readerIndex+length区间内,第一次出现value的位置索引
public abstract int bytesBefore(int length, byte value);
/*
与ByteBufProcessor 设置的条件匹配,第一次匹配成功的位置索引,
ByteBufProcessor**接口**对常用的查询条件进行了抽象:
FIND_CR:CR('\r') , FIND_LN:LN('\n')
*/
public abstract int forEachByteDesc(ByteBufProcessor processor);
- Derived Buffers(提取)
- duplicate
- copy
- copy(int index,int length)
- slice
- 转换成标准NIO的ByteBuffer
- nioBuffer()
- nioBuffer(int index, int length)
- 随机读写(get,set)
ByteBuf源码分析
ByteBuf的主要类图:
从内存分配角度看
- 堆内存(HeapByteBuf)字节缓冲区,特点是
- 内存分配和回收速度快,可以被JVM自动回收
- 缺点是进行Socket I/O时,需要额外做一次内存复制(将堆内存复制到内核的Channel中去)
- 直接内存(DirectByteBuf)字节缓冲区:非堆内存,在堆外进行内存分配(计算机内存),它的特点是:分配和回收速度慢,但是Socket I/O少了一次内存复制,比堆内存快。
经验表明,**使用ByteBuf的最佳实践:**I/O通信线程的读写缓冲区使用DirectByteBuf,后端业务消息的模块使用HeapByteBuf。
从内存回收角度看
- 基于对象池的ByteBuf(PoolxxxByteBuf):可以循环利用,降低高负载时导致的频繁GC,但是内存池的管理和维护复杂。
- 普通ByteBuf(UnpoolxxxByteBuf)
AbstractByteBuf
static final ResourceLeakDetector<ByteBuf> leakDetector = new ResourceLeakDetector<ByteBuf>(ByteBuf.class);
int readerIndex; //读索引
private int writerIndex;//写索引
private int markedReaderIndex; //mark-reader
private int markedWriterIndex;//mark-writer
private int maxCapacity;//最大容量
private SwappedByteBuf swappedBuf;
重点关注leakDetector,它被定义为static,意味着所有的ByteBuf共享一个ResourceLeakDetector<ByteBuf>,ResourceLeakDetector用于检测对象是否泄漏。
AbstractReferenceCountedByteBuf
从类的名称看该类主要对引用进行计数,类似于JVM内存回收的对象引用计数器,用于跟踪对象的分配和销毁,做自动内存回收。
private static final AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> refCntUpdater =
AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
private static final long REFCNT_FIELD_OFFSET;
static {
long refCntFieldOffset = -1;
try {
if (PlatformDependent.hasUnsafe()) {
refCntFieldOffset = PlatformDependent.objectFieldOffset(
AbstractReferenceCountedByteBuf.class.getDeclaredField("refCnt"));
}
} catch (Throwable t) {
// Ignored
}
REFCNT_FIELD_OFFSET = refCntFieldOffset;
}
@SuppressWarnings("FieldMayBeFinal")
private volatile int refCnt = 1;
首先看refCntUpdater ,他是一个AtomicIntegerFieldUpdater类型的原子变量;
REFCNT_FIELD_OFFSET,它用来标识refCnt字段在AbstractReferenceCountedByteBuf的内存地址;
最后定义一个refCnt,用于跟踪对象的引用次数。
ByteBuf相关辅助类
- ByteBufHolder:它是一个ByteBuf容器,针对不同的协议,需要对ByteBuf进行封装和抽象,为此Netty抽象出了ByteBufHolder
- ByteBufAllocator:字节缓冲区分配器,
- PoolByteBufAllocator:基于内存池的字节缓冲分配器
- UnpoolByteBufAllocator:普通的字节缓冲分配器
Channel channel = null;
ByteBufAllocator allocator = channel.alloc();
ChannelHandlerContext ctx = null;
ByteBufAllocator allocator2 = ctx.alloc();
- CompositeByteBuf:复合缓冲区
- Unpooled:
byte[] req = "QUERY TIME ORDER".getBytes();
this.firstMsg = Unpooled.copiedBuffer(req);
- ByteBufUtil它是一个非常又有的工具类,提供了一系列的静态方法。
static ByteBuf encodeString(ByteBufAllocator alloc, CharBuffer src, Charset charset) ;
static String decodeString(ByteBuffer src, Charset charset) ;