工具与资源中心帮助开发者更高效的工作,提供围绕开发者全生命周期的工具与资源https://developer.aliyun.com/...netty入门类用来携带和传递信息的称为ByteBuf。从名字就可以看出这是一个Byte缓冲区。那么ByteBuf有什么特点呢?让我们来看看。ByteBuf详解netty提供了一个io.netty.buffer包,里面定义了各种类型的ByteBuf及其派生类型。nettyBuffer的基础是ByteBuf类,它是一个抽象类。其他的Buffer类基本都是派生自这个类。这个类也定义了netty整体Buffer的基调。先看ByteBuf的定义:publicabstractclassByteBufimplementsReferenceCounted,Comparable{ByteBuf实现了两个接口,分别是ReferenceCounted和Comparable。Comparable是JDK自带的一个接口,意思是类之间可以进行比较。而ReferenceCounted代表对象的引用统计。当一个ReferenceCounted被实例化时,它的引用计数=1,每调用一次retain()方法,计数就会增加,调用release()方法就会减少计数。当计数减少到0时,对象将被释放。如果尝试访问已释放的对象,会报访问异常。如果一个对象实现了ReferenceCounted,而这个对象包含的其他对象也实现了ReferenceCounted,那么当容器对象的count=0时,它里面的其他对象也会通过调用release()方法释放。综上所述,ByteBuf是一个可以统计引用次数的可比较对象。他提供顺序或随机字节访问机制。注意,虽然JDK有自己的ByteBuffer类,但netty中的ByteBuf是ByteBuffer的重新实现。他们没有关系。创建BuffByteBuf是一个抽象类,不能直接实例化。虽然可以使用ByteBuf的子类来实例化,但是netty并不推荐。Netty推荐使用io.netty.buffer.Unpooled创建Buff。Unpooled是一个工具类,可以为ByteBuf分配空间、复制或封装操作。下面是创建多个不同ByteBuf的示例:importstaticio.netty.buffer.Unpooled.*;ByteBufheapBuffer=buffer(128);ByteBufdirectBuffer=directBuffer(256);ByteBufwrappedBuffer=wrappedBuffer(newbyte[128],newbyte[256]);ByteBufcopiedBuffer=copiedBuffer(ByteBuffer.allocate(128));上面我们看到了4种不同的buff构造方法,commonbuff、directBuffer、wrappedBuffer和copiedBuffer。普通buff是固定大小的heapbuff,而directBuffer是固定大小的directbuff。directbuff使用的是堆外内存,省去了向内核拷贝数据,所以效率比普通buff高。WrappedBuffer是对现有字节数组或字节缓冲区的封装。也算是一种观点。当底层数据发生变化时,Wrappedbuffer中的数据也会发生变化。Copiedbuffer是对现有字节数组、字节缓冲区或字符串的深度拷贝,所以它不同于wrappedBuffer,Copiedbuffer和原始数据之间不共享数据。随机访问Buff熟悉集合的朋友应该都知道,要想随机访问一个集合,必须要通过索引来访问。ByteBuf也是如此。可以使用capacity或者获取其capacity,然后使用getByte方法随机访问其中的字节,如下所示://RandomaccessByteBufbuffer=heapBuffer;for(inti=0;i=4){wrappedBuffer.writeInt(newRandom().nextInt());}Discardablebytes已经是读取的字节,其初始值=0,每当readerIndex向右移动时,Discardablebytes的空间就会增加。如果想彻底删除或重置Discardablebytes,可以调用discardReadBytes()方法,该方法会删除Discardablebytes的空间,将多余的空间放在可写字节中,如下所示:调用discardReadBytes()之前:+----------------+----------------+------------------+|可丢弃字节|可读字节|可写字节|+--------------------+-------------+----------------+||||0<=readerIndex<=writerIndex<=capacity调用discardReadBytes()后:+-----------------+--------------------------------------+|可读字节|可写字节(获得更多空间)|+----------------+-----------------------------------+|||readerIndex(0)<=writerIndex(减少)<=capacity注意,虽然可写字节多了,但是它的内容是不可控的,不能保证里面的内容是空的还是不变的。调用clear()方法会将readerIndex和writerIndex清零。注意clear方法只会设置readerIndex和writerIndex的值,不会清除内容。见下图示意图:在调用clear()之前:+------------------+-----------------+--------------------+|可丢弃字节|可读字节|可写字节|+--------------------+----------------+------------------+||||0<=readerIndex<=writerIndex<=capacity调用clear()后:+------------------------------------------------------------+|可写字节(获得更多空间)|+---------------------------------------------------------+||0=readerIndex=writerIndex<=capacitysearchByteBuf提供了单字节查找功能,比如indexOf(int,int,byte)和bytesBefore(int,int,byte)两种方法。如果想对ByteBuf遍历进行查找处理,可以使用forEachByte(int,int,ByteProcessor),该方法接收一个ByteProcessor进行复杂的处理。其他派生缓冲区方法ByteBuf也提供了很多创建派生缓冲区的方法,如下:需要注意的是,这些buf都是在现有buf的基础上衍生出来的,它们的底层内容是一样的,只是readerIndex、writerIndex和标记的index不同。所以他们和原来的buf共享数据。如果要创建新缓冲区,可以使用copy()方法或前面提到的Unpooled.copiedBuffer。在上一节中,我们提到了ByteBuf是一个ReferenceCounted,在推导buf时就是利用了这个特性。我们知道调用retain()方法时,引用计数会增加,但是对于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[]。如果想转成字节数组,可以先调用hasArray()判断,再调用array()方法进行转换。同样的ByteBuf也可以转换成ByteBuffer。可以先调用nioBufferCount()确定可以转换成ByteBuffer的ByteBuffer的个数,再调用nioBuffer()进行转换。返回的ByteBuffer被共享或复制到已有的buf中,返回后buffer的position和limit的修改不会影响到原来的buf。最后,可以使用toString(Charset)方法将ByteBuf转换为String。总结一下,ByteBuf是netty的底层基础,是传输数据的承载对象。深入理解ByteBuf可以帮助你理解netty的设计思想,非常好。