好久木有看代码了,今天把以前读netty源码的时候一直没有看的Buffer部分粗略的看了下,刚开始主要是觉得buffer这个包里面类太多了,觉得比较麻烦,而且相对理解整个netty的设计不太影响,所以就拖着没看,但是记得有一次跟支付宝的技术问了我关于netty中buffer的问题,自己回答的不太好,嗯,该看的还是要看的。。。。
嗯,还是先来一张比较重要的类图:
嗯,上面最后两个类型,UnpooledDirectByteBuf以及UnpooledHeapByteBuf就是netty中最终最常用到的两个buf类型,从名字就能够看出来他们有什么区别,其中一种使用到的内存是在JVM的堆空间上面分配的,其实底层说白了就是一个byte数组,而Direct也就是使用的直接内存,最终它也是在底层用的其实是java的nio中的bytebuffer。
最上层的ByteBuf是一个抽象类,它定义了很多的抽象方法,这些方法就是netty中bytebuf暴露的所有api,功能还是蛮强大的,netty之所以自己重头定义了整套的bytebuf就是因为nio类库中原生的buffer功能太弱的原因。。。。
这里面比较有意思的方法有如下:
(1)discardReadBytes,它用于对buf里面的数据进行平移,将已经读取的数据抛弃,并重用这部分内存空间,用如下这张图来说明:
(2)它定义的read和write方法中,有多种多样的源以及目的类型,甚至还有channel类型,这就意味着在buf中还是实现从流或者从channel中读取数据的具体操作。。。。
剩下的就还有很多buf常规的方法了,例如读取一个字节,读取一个整形什么的。。。。
好了,上面介绍了最终在netty中最常用的两种buf类型,从名字也可以看出他们不是pool的,在netty中还有一种pool的,实现的比较复杂,但是当然这种效率很高了,不过在实际的代码中基本上还是用的unpool的,好啦,而且除了在最开始unsafe对象中从channel中读取数据的时候常用directbuf外,其余一般都还是用的undirect的内存。。。
那么我们在编程的时候怎么使用buf呢?一般情况下都是使用一个工具类Unpooled,它定义了很多静态方法,编程时可以用它来生成buf,例如我们经常用如下的形式来生成一个bytebuf对象:
Unpooled.buffer(1000);
嗯,这段代码的意思就是生成1000个字节的buf对象,这里默认是分配undirect的内存,如果要分配direct的,那有专门的方法供调用。。。。
但是其实Unpooled最终也是调用ByteBufAllocator来创建buf对象的,他们之间的关系如下:
这里ByteBufAllocator对象默认是UnpooledByteBufAllocator对象,我们可以来看看它的类型定义:
public final class UnpooledByteBufAllocator extends AbstractByteBufAllocator { /** * Default instance */ public static final UnpooledByteBufAllocator DEFAULT = new UnpooledByteBufAllocator(PlatformDependent.directBufferPreferred()); /** * Create a new instance * * @param preferDirect {@code true} if {@link #buffer(int)} should try to allocate a direct buffer rather than * a heap buffer */ public UnpooledByteBufAllocator(boolean preferDirect) { super(preferDirect); } @Override protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) { return new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity); } @Override protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) { if (PlatformDependent.hasUnsafe()) { return new UnpooledUnsafeDirectByteBuf(this, initialCapacity, maxCapacity); } else { return new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity); } } @Override public boolean isDirectBufferPooled() { return false; } }
其实还是蛮简单的,也主要是实现了两个生成buf的方法,分别是direct和undirect的。。。。另外它导出了一个static的本类型对象,七班情况下也就是使用这个对象来分配buf对象。。。
另外在它的父类中定义了很多的分配方法,最基本的有创建heap的buf,也就是undirect的,还有创建direct的buf,另外还有自适应的方法,通过判断当前的系统类型,自动的来创建这两种类型的buf中的一种。。。。
另外UnpooledByteBufAllocator其实还是channel的默认的buf分配器。。。来看如下的这段代码:
final ByteBufAllocator allocator = config.getAllocator(); //buffer的allocater final int maxMessagesPerRead = config.getMaxMessagesPerRead(); boolean closed = false; Throwable exception = null; ByteBuf byteBuf = null; int messages = 0; try { for (;;) { byteBuf = allocHandle.allocate(allocator); int localReadAmount = doReadBytes(byteBuf); //将数据读进来
这段代码就是在channel可以读取的情况的时候,调用内部的unsafe对象来读取数据的时候要做的操作,这里首先是获取ByteBufAllocator对象,待会将会用它来生成用于存放这次读出来的数据buf,而这最终将会调用UnpooledByteBufAllocator自适应的方法来生成buf,最终这里一般情况下生成的是direct类型的buf。。。。。
好啦,到这里其实对netty中buf的设计有了比较粗略的大致了解,不再是以前那种模模糊糊的了。。。
总的来说实现的还算比较的简单吧。。。。而且用起来也还算是比较的方便。。。