欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Netty的ByteBuf介绍

程序员文章站 2022-06-10 20:37:33
...

Netty的ByteBuf缓冲区实现地比Java本身的ByteBuffer更加灵活,方便。它的类结构也比较复杂,这里只说ByteBuf核心的几个要点。

1. 最重要的是要理解为什么要ByteBuf这个组件。主要还是因为基于select / poll / epoll这种IO多路复用技术的NIO是非阻塞同步IO的模型,由于是同步IO,需要用户线程自己来处理IO的读写,由于是非阻塞的,每次调用read, write读写的字节数是不确定的,所以非阻塞同步IO必须有缓冲区这个组件来保存每次读写的中间状态,通过缓冲区来确定是否读写完成。更多内容请参考http://blog.csdn.net/iter_zc/article/details/39291647

2. ByteBuf不是对ByteBuffer的封装,而是重新实现了一个缓冲区。ByteBuffer只使用了一个position指针来记录当前的读写位置,ByteBuf使用了两个指针readerIndex, writerIndex分别来记录当前的读写位置,使用起来更加简单和方便。

3. ByteBuffer是一个固定长度的缓冲区,当put方法要写的数据大于可写的容量时会抛出异常。ByteBuf改进了这个设计,支持自动扩容。每次put之前会检查是否可以完全写入,如果不能,就会自动扩展ByteBuf的容量,保证put方法不会抛出异常。

public ByteBuf writeInt(int value) {	ensureWritable(4);	_setInt(writerIndex, value);	writerIndex += 4;	return this;} public ByteBuf ensureWritable(int minWritableBytes) {	if (minWritableBytes < 0) {	throw new IllegalArgumentException(String.format(		"minWritableBytes: %d (expected: >= 0)", minWritableBytes));	}	if (minWritableBytes <= writableBytes()) {	return this;	}	if (minWritableBytes > maxCapacity - writerIndex) {	throw new IndexOutOfBoundsException(String.format(		"writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",		writerIndex, minWritableBytes, maxCapacity, this));	}	// Normalize the current capacity to the power of 2.	int newCapacity = calculateNewCapacity(writerIndex + minWritableBytes);	// Adjust to the new capacity.	capacity(newCapacity);	return this;}//UnpooledHeapByteBuf的capacity方法来自动扩容public ByteBuf capacity(int newCapacity) {	ensureAccessible();	if (newCapacity < 0 || newCapacity > maxCapacity()) {	throw new IllegalArgumentException("newCapacity: " + newCapacity);	}	int oldCapacity = array.length;	if (newCapacity > oldCapacity) {	byte[] newArray = new byte[newCapacity];	System.arraycopy(array, 0, newArray, 0, array.length);	setArray(newArray);	} else if (newCapacity < oldCapacity) {	byte[] newArray = new byte[newCapacity];	int readerIndex = readerIndex();	if (readerIndex < newCapacity) {		int writerIndex = writerIndex();		if (writerIndex > newCapacity) {		writerIndex(writerIndex = newCapacity);		}		System.arraycopy(array, readerIndex, newArray, readerIndex, writerIndex - readerIndex);	} else {		setIndex(newCapacity, newCapacity);	}	setArray(newArray);	}	return this;}private void setArray(byte[] initialArray) {	array = initialArray;	tmpNioBuf = null;}

4. 和ByteBuffer一样,ByteBuf也支持堆内缓冲区和堆外直接缓冲区,根据经验来说,底层IO处理线程的缓冲区使用堆外直接缓冲区,减少一次IO复制。业务消息的编解码使用堆内缓冲区,分配效率更高,而且不涉及到内核缓冲区的复制问题。

5. ByteBuf的堆内缓冲区又分为内存池缓冲区PooledByteBuf和普通内存缓冲区UnpooledHeapByteBuf。PooledByteBuf采用二叉树来实现一个内存池,集中管理内存的分配和释放,不用每次使用都新建一个缓冲区对象。UnpooledHeapByteBuf每次都会新建一个缓冲区对象。在高并发的情况下推荐使用PooledByteBuf,可以节约内存的分配。在性能能够保证的情况下,可以使用UnpooledHeapByteBuf,实现比较简单。