netty中ByteBuf部分的分析

作者:fjs_cloud 和linux相关  

好久木有看代码了,今天把以前读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的设计有了比较粗略的大致了解,不再是以前那种模模糊糊的了。。。

总的来说实现的还算比较的简单吧。。。。而且用起来也还算是比较的方便。。。

相关资料:

netty中ByteBuf部分的分析来源网络,如有侵权请告知,即处理!

编程Tags: