美文网首页Java 杂谈深入浅出Netty源码剖析
【第10篇】Netty自适应缓冲区分配策略源码分析

【第10篇】Netty自适应缓冲区分配策略源码分析

作者: 爱学习的蹭蹭 | 来源:发表于2019-05-25 20:04 被阅读0次

    Netty底层的策略

    • Netty底层提供了扩容,涉及数据拷贝,可预测的缓冲区处理
    • C语言的分配内存有Malloc实现

    AdaptiveRecvByteBufAllocator

    • AdaptiveRecvByteBufAllocator根据返回的大小会自动增或减可预测的Buffer,基于之前的读与写的对比的结构的调节大小
    AdaptiveRecvByteBufAllocator关系图
    • 该RecvByteBufAllocator在反馈时自动增加和减少预测的缓冲区大小。
      如果前面的读操作完全填满了分配的缓冲区,那么它将逐渐增加预期的可读字节数。如果read操作不能连续两次填充分配的缓冲区的一定数量,则会逐渐减少预期的可读字节数。否则,它将不断返回相同的预测。

    • RecvByteBufAllocator源码备注

     //静态代码块的作用是对SIZE_TABLE数组填写1~38的坐标的值是16,32,48....一直到65536
        //自动减少或者增加的幅度就是来自于这个数组。具体逻辑在HandleImpl对的record方法。
        static {
            List<Integer> sizeTable = new ArrayList<Integer>();
            for (int i = 16; i < 512; i += 16) {
                sizeTable.add(i);//1~16的设置是16到(512-16)
            }
    
            for (int i = 512; i > 0; i <<= 1) {
                sizeTable.add(i);//从512到65536
            }
    
            SIZE_TABLE = new int[sizeTable.size()];
            for (int i = 0; i < SIZE_TABLE.length; i ++) {
                SIZE_TABLE[i] = sizeTable.get(i);//填写到SIZE_TABLE数组
            }
        }
    
    //句柄实现
    private final class HandleImpl extends MaxMessageHandle {
            private final int minIndex;//最小索引
            private final int maxIndex;//最大索引
            private int index;//索引
            private int nextReceiveBufferSize;//下一个接收缓存区的大小
            private boolean decreaseNow;//减少的标识
    
            public HandleImpl(int minIndex, int maxIndex, int initial) {
                this.minIndex = minIndex;
                this.maxIndex = maxIndex;
                index = getSizeTableIndex(initial); //获取表里面的索引大小
                nextReceiveBufferSize = SIZE_TABLE[index];
            }
            //预估处理接收值
            @Override
            public int guess() {
                return nextReceiveBufferSize;
            }
            //处理记录实际的字节
            private void record(int actualReadBytes) {
                //判断实际读取字节与最大索引值
                if (actualReadBytes <= SIZE_TABLE[Math.max(0, index - INDEX_DECREMENT - 1)]) {
                    if (decreaseNow) {
                        //获取最大索引
                        index = Math.max(index - INDEX_DECREMENT, minIndex);
                        nextReceiveBufferSize = SIZE_TABLE[index];//获取下一个接收缓存大小
                        decreaseNow = false;
                    } else {
                        decreaseNow = true;
                    }
                } else if (actualReadBytes >= nextReceiveBufferSize) {
                   //获取最小索引
                    index = Math.min(index + INDEX_INCREMENT, maxIndex);
                    nextReceiveBufferSize = SIZE_TABLE[index];
                    decreaseNow = false;
                }
            }
    
            //完成读写记录
            @Override
            public void readComplete() {
                record(totalBytesRead());
            }
        }
    
    HandleImpl
    • HandleImpl类是继承MaxMessageHandle,然后跟踪到MaxMessageHandle,可以看出来此类有如下代码,这里只列部分。
     public abstract class MaxMessageHandle implements ExtendedHandle {
            private ChannelConfig config;//管道配置变量
            private int maxMessagePerRead;//最大预期消息读变量
            private int totalMessages;//总消息变量
            private int totalBytesRead;//总读的总字节变量
            private int attemptedBytesRead;//尝试去读的字节变量
            private int lastBytesRead;//最后字节读变量
            //不检测提供者尝试读取的字节
            private final UncheckedBooleanSupplier defaultMaybeMoreSupplier = new UncheckedBooleanSupplier() {
                @Override
                public boolean get() {
                    return attemptedBytesRead == lastBytesRead;
                }
            };
    
            /**
             *  重置ChannelConfig 管道配置
             */
            @Override
            public void reset(ChannelConfig config) {
                this.config = config;
                maxMessagePerRead = maxMessagesPerRead();
                totalMessages = totalBytesRead = 0;
            }
    
            //分配字节缓存器处理,此类比较重要
            @Override
            public ByteBuf allocate(ByteBufAllocator alloc) {
                return alloc.ioBuffer(guess());
            }
     
           tedBytesRead = bytes;
            }
             //分配字节读
            protected final int totalBytesRead() {
                return totalBytesRead < 0 ? Integer.MAX_VALUE : totalBytesRead;
            }
        }
    
    ByteBuf
    • ByteBufAllocator

    ByteBufAllocator展开里面的方法,可以看出来非常重要的一个类,里面包含ioBuffer(IO缓冲区),directBuffer(直接缓冲区),heapBuffer(堆缓冲区),compositeBuffer(复合缓冲区),compositeDirectBuffer(复合直接缓冲区)compositeHeapBuffer(复合堆缓冲区),然后在展开的类的方法有一个calculateNewCapacity方法,此方法尤为重要,计算ByteBuf的新容量,当ByteBuf需要以maxCapacity为上限扩展minNewCapacity时使用该容量。

    • calculateNewCapacity方法,由抽象AbstractByteBufAllocator和PreferHeapByteBufAllocator进行实现.
      calculateNewCapacity
    • AbstractByteBufAllocator又由PooledByteBufAllocatorUnpooledByteBufAllocator(池化的字节缓冲区分配器)(无池化的字节缓冲区分配器)进行底层的实现,
    AbstractByteBufAllocator

    PooledByteBufAllocator和UnpooledByteBufAllocator关系图

    PooledByteBufAllocator UnpooledByteBufAllocator

    运算符

    • << = 1 表示向左移一位 X 2 sizeTable分配可分配的大小Buffer

    线程

    • 线程本身不能运行,只能在程序上运行 一个Socket一个线程,线程开销大,主要是上下文切换的原因

    值传递和引用传递

    • 值传递是对基本类型变量而言的,传递的是该变量的一个副本改变副本不影响原来变量
    • 引用传递一般是对对象类型变量而言,传递的是该对象地址的一个副本,并不是原对象本身
    • 一般认为Java内的传递都是值传递,Java中实例对象的传递是引用传递

    相关文章

      网友评论

        本文标题:【第10篇】Netty自适应缓冲区分配策略源码分析

        本文链接:https://www.haomeiwen.com/subject/xlnazqtx.html