netty中ByteBuf部分的分析

来源:互联网 发布:关于网络舆论的论文 编辑:程序博客网 时间:2024/05/22 04:54

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

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

1 0