导读
原创文章,转载请注明出处。
本文源码地址: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框架、分布式调用跟踪、监控系统等。爱技术、爱学习,欢迎联系交流。