导读
原创文章,转载请注明出处。
本文源码地址:netty-source-code-analysis
本文所使用的netty版本4.1.6.Final:带注释的netty源码
本文简要地介绍ByteBuf的结构、主要api和创建方法。
1 ByteBuf的结构
每一个ByteBuf都有一个可容纳的字节上限叫capacity。在ByteBuf中通过两个指针readerIndex和writerIndex将整个个ByteBuf划分成3个部分,分别是已丢弃部分、可读部分和可写部分,示意图如下。
+-------------------+------------------+------------------+
| 已丢弃 | 可读 | 可写 |
| | (CONTENT) | |
+-------------------+------------------+------------------+
| | | |
0 <= readerIndex <= writerIndex <= capacity
readerIndex、writerIndex和capacity之间满足简单地数学关系0 <= readerIndex <= writerIndex <= capcity。
一个新建的从未进行过读写操作的ByteBuf,readerIndex和writerIndex都为0。整个空间都是可写部分。
+---------------------------------------------------------+
| 可写 |
| |
+---------------------------------------------------------+
| |
0 capacity
readerIndex
writerIndex
现在往其中写入一些数据,写数据的过程会引起writerIndex的移动,writerIndex移动的最大值为capacity,写数据的过程中readerIndex保持不变。
+--------------------------------------+------------------+
| 可读 | 可写 |
| (CONTENT) | |
+--------------------------------------+------------------+
| | |
0 <= writerIndex <= capacity
readerIndex
接着读取一些数据,读数据的过程会引起readerIndex的移动,readerIndex的最大值为writerIndex,在读数据的过程中writerIndex保持不变。已经被读取过的部分就成了已丢弃部分。
+-------------------+------------------+------------------+
| 已丢弃 | 可读 | 可写 |
| | (CONTENT) | |
+-------------------+------------------+------------------+
| | | |
0 <= readerIndex <= writerIndex <= capacity
2 ByteBuf的主要方法
2.1 write族方法
write族方法用来向ByteBuf中写入各种类型的数据,比如writeBytes、writeChar、writeInt等。另外支持写入小端字节序数据,比如writeIntLE、writeLongLE等。
write族方法的调用时写入数据的起始位置就是当前writerIndex指向的位置,写入数据会引起writerIndex的移动。
2.2 read族方法
read族方法用来从ByteBuf中读取数据,比如readBytes、writeBytes、writeLong等。同样也支持读取小端字节序数据,比如readIntLE、readLongLE等。
read族方法的调用会引起readerIndex的移动。
2.3 set族方法
和write族方法一样,set族方法同样用来向ByteBuf中写入各种类型的数据。write能写入的数据set也能,比如writeInt和setInt。不同的是set族方法在调用时需要传递一个索引参数,也就是说需要指定写入数据的位置,比如writeInt(int value)和setInt(int index, int value)。
set族方法的调用不会引起writerIndex的移动。
2.4 get族方法
和read族方法一样,get族方法同样用来从ByteBuf中读取数据。比如有getInt、getLong方法。与read族方法不一样的是在调用时需要传递一个索引参数,也就是说需要指定读取数据的位置,比如getInt(int index)和readInt()。
read族方法的调用不会引起readerIndex的移动。
2.5 读取/设置 Index的方法
ByteBuf提供了writerIndex()和readerIndex()方法分别可以返回当前的writerIndex和readerIndex。
除了read和write方法可以改变readerIndex和writerIndex,ByteBuf也提供了可以手动设置readerIndex的readerIndex(int readerIndex)及手动设置writerIndex的writerIndex(int writerIndex)方法。
2.6 slice方法
slice方法将当前ByteBuf的可读数据区映射到一个新的ByteBuf,并返回这个新的ByteBuf。这个新的ByteBuf与原ByteBuf共享数据区域,但是拥有独立的readerIndex和writerIndex。
也提供了slice(int index, int length)方法可以指定映射的数据区域范围。
对新的ByteBuf数据的修改同样会影响到的原来的ByteBuf的数据,反之亦然。
2.7 duplicate方法
duplicate方法将当前ByteBuf的整个数据区映射到一个新的ByteBuf,并返回这个新的ByteBuf。这个新的ByteBuf与原ByteBuf共享数据区域,但是拥有独立的readerIndex和writerIndex。
对新的ByteBuf数据的修改同样会影响到的原来的ByteBuf的数据,反之亦然。
2.8 copy方法
copy方法返回当前ByteBuf的一个复制品,新的ByteBuf拥有与原来的ByteBuf不一样的数据区域,readerIndex和writerIndex也是独立的。
同样提供了copy(int index, int length)方法可以指定复制的数据区域范围。
对新的ByteBuf数据的修改不会影响到原来的ByteBuf,反之亦然。
2.9 retain和release方法
ByteBuf使用引用计数法来表示当前ByteBuf被引用的次数,如果一个ByteBuf的被引用次数为0,则释放该ByteBuf对应的内存。
3 ByteBuf的主要实现及创建
ByteBuf的主要实现类图如下图所示,总体上分为Pooled和UnPooled两类,顾名思义为池化的和非池化的,又根据分配的是直接内存还是堆内存分为HeapByteBuf和DirectByteBuf。

3.1 通过ByteBufAllocator创建
ByteBufAllocator有两个实现分别为PooledByteBufAllocator和UnpooledByteBufAllocator对应分配出来是的PooledByteBuf和UnpooledByteBuf。
PooledByteBufAllocator将在后续的文章中重点分析,而UnpooledByteBufAllocator则比较简单,不再赘述。
3.2 通过包裹ByteBuffer及byte[]创建
使用Unpooled.wrappedBuffer方法可以对jdkButeBuffer及byte[]进行包裹创建出一个UnpooledByteBuf。
4 总结
ByteBuf主要api有read/write,get/set,slice,duplicate,copy等系列方法。实现类上主要分为Unpooled和Pooled,并且支持分配HeapByteBuf及DirectByteBuf。
关于作者
王建新,转转架构部服务治理负责人,主要负责服务治理、RPC框架、分布式调用跟踪、监控系统等。爱技术、爱学习,欢迎联系交流。










