工具与资源核心
帮忙开发者更加高效的工作,提供围绕开发者全生命周期的工具与资源
https://developer.aliyun.com/...
简介
netty中用于进行信息承载和交换的类叫做ByteBuf,从名字能够看出这是Byte的缓存区,那么ByteBuf都有哪些个性呢?一起来看看。
ByteBuf详解
netty提供了一个io.netty.buffer的包,该包外面定义了各种类型的ByteBuf和其衍生的类型。
netty Buffer的根底是ByteBuf类,这是一个抽象类,其余的Buffer类基本上都是由该类衍生而得的,这个类也定义了netty整体Buffer的基调。
先来看下ByteBuf的定义:
public abstract class ByteBuf implements ReferenceCounted, Comparable<ByteBuf> {
ByteBuf实现了两个接口,别离是ReferenceCounted和Comparable。Comparable是JDK自带的接口,示意该类之间是能够进行比拟的。而ReferenceCounted示意的是对象的援用统计。当一个ReferenceCounted被实例化之后,其援用count=1,每次调用retain() 办法,就会减少count,调用release() 办法又会缩小count。当count减为0之后,对象将会被开释,如果试图拜访被开释过后的对象,则会报拜访异样。
如果一个对象实现了ReferenceCounted,并且这个对象外面蕴含的其余对象也实现了ReferenceCounted,那么当容器对象的count=0的时候,其外部的其余对象也会被调用release()办法进行开释。
综上,ByteBuf是一个能够比拟的,能够计算援用次数的对象。他提供了序列或者随机的byte拜访机制。
留神的是,尽管JDK中有自带的ByteBuffer类,然而netty中的 ByteBuf 算是对Byte Buffer的从新实现。他们没有关联关系。
创立一个Buff
ByteBuf是一个抽象类,并不能间接用来实例化,尽管能够应用ByteBuf的子类进行实例化操作,然而netty并不举荐。netty举荐应用io.netty.buffer.Unpooled来进行Buff的创立工作。Unpooled是一个工具类,能够为ByteBuf调配空间、拷贝或者封装操作。
上面是创立几个不同ByteBuf的例子:
import static io.netty.buffer.Unpooled.*;
ByteBuf heapBuffer = buffer(128);
ByteBuf directBuffer = directBuffer(256);
ByteBuf wrappedBuffer = wrappedBuffer(new byte[128], new byte[256]);
ByteBuf copiedBuffer = copiedBuffer(ByteBuffer.allocate(128));
下面咱们看到了4种不同的buff构建形式,一般的buff、directBuffer、wrappedBuffer和copiedBuffer。
一般的buff是固定大小的堆buff,而directBuffer是固定大小的direct buff。direct buff应用的是堆外内存,省去了数据到内核的拷贝,因而效率比一般的buff要高。
wrappedBuffer是对现有的byte arrays或者byte buffers的封装,能够看做是一个视图,当底层的数据发生变化的时候,Wrapped buffer中的数据也会发生变化。
Copied buffer是对现有的byte arrays、byte buffers 或者 string的深拷贝,所以它和wrappedBuffer是不同的,Copied buffer和原数据之间并不共享数据。
随机拜访Buff
相熟汇合的敌人应该都晓得,要想随机拜访某个汇合,肯定是通过index来拜访的,ByteBuf也一样,能够通过capacity或得其容量,而后通过getByte办法随机拜访其中的byte,如下所示:
//随机拜访
ByteBuf buffer = heapBuffer; for (int i = 0; i < buffer.capacity(); i ++) { byte b = buffer.getByte(i); System.out.println((char) b); }
序列读写
读写要比拜访简单一点,ByteBuf 提供了两个index用来定位读和写的地位,别离是readerIndex 和 writerIndex ,两个index别离管制读和写的地位。
下图显示的一个buffer被分成了三局部,别离是可废除的bytes、可读的bytes和可写的bytes。 |
---|
| discardable bytes | readable bytes | writable bytes || | (CONTENT) | |+-------------------+------------------+------------------+| | | |0 <= readerIndex <= writerIndex <= capacity
上图还表明了readerIndex、writerIndex和capacity的大小关系。
其中readable bytes是真正的内容,能够通过调用read 或者skip 的办法来进行拜访或者跳过,调用这些办法的时候,readerIndex会同步减少,如果超出了readable bytes的范畴,则会抛出IndexOutOfBoundsException。默认状况下readerIndex=0。
上面是一个遍历readable bytes的例子:
//遍历readable bytes
while (directBuffer.isReadable()) { System.out.println(directBuffer.readByte()); }
首先通过判断是否是readable来决定是否调用readByte办法。
Writable bytes是一个未确定的区域,期待被填充。能够通过调用write*办法对其操作,同时writerIndex 会同步更新,同样的,如果空间不够的话,也会抛出IndexOutOfBoundsException。默认状况下 新调配的writerIndex =0 ,而wrapped 或者copied buffer的writerIndex=buf的capacity。
上面是一个应用writable Byte的例子:
//写入writable bytes
while (wrappedBuffer.maxWritableBytes() >= 4) { wrappedBuffer.writeInt(new Random().nextInt()); }
Discardable bytes是曾经被读取过的bytes,初始状况下它的值=0,每当readerIndex右移的时候,Discardable bytes的空间就会减少。如果想要齐全删除或重置Discardable bytes,则能够调用discardReadBytes()办法,该办法会将Discardable bytes空间删除,将多余的空间放到writable bytes中,如下所示:
调用 discardReadBytes() 之前:
+-------------------+------------------+------------------+| discardable bytes | readable bytes | writable bytes |+-------------------+------------------+------------------+| | | |0 <= readerIndex <= writerIndex <= capacity
调用 discardReadBytes()之后:
+------------------+--------------------------------------+| readable bytes | writable bytes (got more space) |+------------------+--------------------------------------+| | |
readerIndex (0) <= writerIndex (decreased) <= capacity
留神,尽管writable bytes变多了,然而其内容是不可控的,并不能保障外面的内容是空的或者不变。
调用clear()办法会将readerIndex 和 writerIndex 清零,留神clear办法只会设置readerIndex 和 writerIndex 的值,并不会清空content,看上面的示意图:
调用 clear()之前:
+-------------------+------------------+------------------+| discardable bytes | readable bytes | writable bytes |+-------------------+------------------+------------------+| | | |0 <= readerIndex <= writerIndex <= capacity
调用 clear()之后:
+---------------------------------------------------------+| writable bytes (got more space) |+---------------------------------------------------------+| |0 = readerIndex = writerIndex <= capacity
搜寻
ByteBuf提供了单个byte的搜寻性能,如 indexOf(int, int, byte) 和 bytesBefore(int, int, byte)两个办法。
如果是要对ByteBuf遍历进行搜寻解决的话,能够应用 forEachByte(int, int, ByteProcessor),这个办法接管一个ByteProcessor用于进行简单的解决。
其余衍生buffer办法
ByteBuf还提供了很多办法用来创立衍生的buffer,如下所示:
duplicate()
slice()
slice(int, int)
readSlice(int)
retainedDuplicate()
retainedSlice()
retainedSlice(int, int)
readRetainedSlice(int)
要留神的是,这些buf是建设在现有buf根底上的衍生品,他们的底层内容是一样的,只有readerIndex, writerIndex 和做标记的index不一样。所以他们和原buf是有共享数据的。如果你心愿的是新建一个全新的buffer,那么能够应用copy()办法或者后面提到的Unpooled.copiedBuffer。
在后面大节中,咱们讲到ByteBuf是一个ReferenceCounted,这个特色在衍生buf中就用到了。咱们晓得调用retain() 办法的时候,援用count会减少,然而对于 duplicate(), slice(), slice(int, int) 和 readSlice(int) 这些办法来说,尽管他们也是援用,然而没有调用retain()办法,这样原始数据会在任意一个Buf调用release()办法之后被回收。
如果不想有下面的副作用,那么能够将办法替换成retainedDuplicate(), retainedSlice(), retainedSlice(int, int) 和 readRetainedSlice(int) ,这些办法会调用retain()办法以减少一个援用。
和现有JDK类型的转换
之前提到了ByteBuf 是对ByteBuffer的重写,他们是不同的实现。尽管这两个不同,然而不障碍将ByteBuf转换ByteBuffer。
当然,最简略的转换是把ByteBuf转换成byte数组byte[]。要想转换成byte数组,能够先调用hasArray() 进行判断,而后再调用array()办法进行转换。
同样的ByteBuf还能够转换成为ByteBuffer ,能够先调用 nioBufferCount()判断可能转换成为 ByteBuffers的个数,再调用nioBuffer() 进行转换。
返回的ByteBuffer是对现有buf的共享或者复制,对返回之后buffer的position和limit批改不会影响到原buf。
最初,应用toString(Charset) 办法能够将ByteBuf转换成为String。
总结
ByteBuf是netty的底层根底,是传输数据的承载对象,深刻了解ByteBuf就能够搞懂netty的设计思维,十分不错。