zoukankan      html  css  js  c++  java
  • Netty源码分析第3章(客户端接入流程)---->第1节: 初始化NioSockectChannelConfig

     

    Netty源码分析第三章: 客户端接入流程

     

    概述:

            之前的章节学习了server启动以及eventLoop相关的逻辑, eventLoop轮询到客户端接入事件之后是如何处理的?这一章我们循序渐进, 带大家继续剖析客户端接入之后的相关逻辑

     

    第一节:初始化NioSockectChannelConfig

     

     

    在剖析接入流程之前我们首先补充下第一章有关创建channel的知识:

     

    我们在第一章剖析过channel的创建, 其中NioServerSocketChannel中有个构造方法:

    public NioServerSocketChannel(ServerSocketChannel channel) {
        super(null, channel, SelectionKey.OP_ACCEPT);
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    }

    当时我们并没有剖析config相关知识, 在这一章首先对此做一个补充, 这里我们看到每一个NioServerSocketChannel都拥有一个config属性, 这个属性存放着NioServerSocketChannel的相关配置, 这里创建一个NioServerSocketChannelConfig对象, 并将当前channel, 和channel对应的java底层的socket对象进行了传入, NioServerSocketChannelConfig其实是NioServerSocketChannel的内部类

    我们跟到NioServerSocketChannelConfig类的构造方法中:

    private NioServerSocketChannelConfig(NioServerSocketChannel channel, ServerSocket javaSocket) {
        super(channel, javaSocket);
    }

    我们继续跟入其父类DefaultServerSocketChannelConfig的构造方法中:

    public DefaultServerSocketChannelConfig(ServerSocketChannel channel, ServerSocket javaSocket) {
        super(channel);
        if (javaSocket == null) {
            throw new NullPointerException("javaSocket");
        }
        this.javaSocket = javaSocket;
    }

    这里继续调用了其父类的构造方法, 并保存了jdk底层的socket对象, 并且调用其父类DefaultChannelConfig的构造方法

    跟到其父类DefaultChannelConfig的构造方法中:

    public DefaultChannelConfig(Channel channel) {
        this(channel, new AdaptiveRecvByteBufAllocator());
    }

    这里调用了自身的构造方法, 传入了channel和一个AdaptiveRecvByteBufAllocator对象

    AdaptiveRecvByteBufAllocator是一个缓冲区分配器, 用于分配一个缓冲区Bytebuf的, 有关Bytebuf的相关内容会在后面的章节详细讲解, 这里可以简单介绍作为了解, 就当对于之后知识的预习

    Bytebuf相当于jdk的ByetBuffer, Netty对其做了重新的封装, 用于读写channel中的字节流, 熟悉Nio的同学对此应该并不陌生, AdaptiveRecvByteBufAllocator就是用于分配netty中ByetBuff的缓冲区分配器, 根据名字, 我们不难看出这个缓冲区是一个可变大小的字节缓冲区

    我们跟到AdaptiveRecvByteBufAllocator的构造方法中:

    public AdaptiveRecvByteBufAllocator() {
        //DEFAULT_MINIMUM:最小缓冲区长度64字节
        //DEFAULT_INITIAL:初始容量1024字节
        //最大容量65536字节
        this(DEFAULT_MINIMUM, DEFAULT_INITIAL, DEFAULT_MAXIMUM);
    }

    这里调用自身的构造方法并且传入了三个属性, 这三个属性的含义分别为:

    DEFAULT_MINIMUM:代表要分配的缓冲区长度最少为64个字节

    DEFAULT_INITIAL:代表要分配的缓冲区的初始容量为1024个字节

    DEFAULT_MAXIMUM:代表要分配的缓冲区最大容量为65536个字节

    我们跟到this(DEFAULT_MINIMUM, DEFAULT_INITIAL, DEFAULT_MAXIMUM)方法中

    public AdaptiveRecvByteBufAllocator(int minimum, int initial, int maximum) {
        
        //忽略验证代码
    
        //最小容量在table中的下标
        int minIndex = getSizeTableIndex(minimum);
        if (SIZE_TABLE[minIndex] < minimum) {
            this.minIndex = minIndex + 1;
        } else {
            this.minIndex = minIndex;
        }
    
        //最大容量在table中的下标
        int maxIndex = getSizeTableIndex(maximum);
        if (SIZE_TABLE[maxIndex] > maximum) {
            this.maxIndex = maxIndex - 1;
        } else {
            this.maxIndex = maxIndex;
        }
    
        this.initial = initial;
    }

    其中这里初始化了三个属性, 分别是:

    minIndex:最小容量在size_table中的下标

    maxIndex:最大容量在table中的下标

    initial:初始容量1024个字节

    这里的size_table就是一个数组, 里面盛放着byteBuf可分配的内存大小的集合, 分配的bytebuf无论是扩容还是收缩, 内存大小都属于size_table中的元素, 那么这个数组是如何初始化的, 我们跟到这个属性中:

    private static final int[] SIZE_TABLE;

     

    我们看到是一个final修饰的静态成员变量, 我们跟到static块中看它的初始化过程:

    static {
        //List集合
        List<Integer> sizeTable = new ArrayList<Integer>();
        //从16开始, 每递增16添加到List中, 直到大于等于512
        for (int i = 16; i < 512; i += 16) {
            sizeTable.add(i);
        }
        //从512开始, 倍增添加到List中, 直到内存溢出
        for (int i = 512; i > 0; i <<= 1) {
            sizeTable.add(i);
        }
        //初始化数组
        SIZE_TABLE = new int[sizeTable.size()];
        //将list的内容放入数组中
        for (int i = 0; i < SIZE_TABLE.length; i ++) {
            SIZE_TABLE[i] = sizeTable.get(i);
        }
    }

    首先创建一个Integer类型的list用于盛放内存元素

    这里通过两组循环为list添加元素

    首先看第一组循环:

    for (int i = 16; i < 512; i += 16) {
        sizeTable.add(i);
    }

    这里是通过16平移的方式, 直到512个字节, 将每次平移之后的内存大小添加到list中

    再看第二组循环

    for (int i = 512; i > 0; i <<= 1) {
        sizeTable.add(i);
    }

    超过512之后, 再通过倍增的方式循环, 直到int类型内存溢出, 将每次倍增之后大小添加到list中

    最后初始化SIZE_TABLE数组, 将list中的元素按下表存放到数组中

    这样就初始化了内存数组

    再回到AdaptiveRecvByteBufAllocator的构造方法中:

    public AdaptiveRecvByteBufAllocator(int minimum, int initial, int maximum) {
        
        //忽略验证代码
    
        //最小容量在table中的下标
        int minIndex = getSizeTableIndex(minimum);
        if (SIZE_TABLE[minIndex] < minimum) {
            this.minIndex = minIndex + 1;
        } else {
            this.minIndex = minIndex;
        }
    
        //最大容量在table中的下标
        int maxIndex = getSizeTableIndex(maximum);
        if (SIZE_TABLE[maxIndex] > maximum) {
            this.maxIndex = maxIndex - 1;
        } else {
            this.maxIndex = maxIndex;
        }
    
        this.initial = initial;
    }

    这里分别根据传入的最小和最大容量去SIZE_TABLE中获取其下标

    我们跟到getSizeTableIndex(minimum)中:

    private static int getSizeTableIndex(final int size) {
        for (int low = 0, high = SIZE_TABLE.length - 1;;) {
            if (high < low) {
                return low;
            }
            if (high == low) {
                return high;
            }
    
            int mid = low + high >>> 1;
            int a = SIZE_TABLE[mid];
            int b = SIZE_TABLE[mid + 1];
            if (size > b) {
                low = mid + 1;
            } else if (size < a) {
                high = mid - 1;
            } else if (size == a) {
                return mid;
            } else {
                return mid + 1;
            }
        }
    }

    这里是通过二分查找去获取其下表

    if (SIZE_TABLE[minIndex] < minimum)这里判断最小容量下标所属的内存大小是否小于最小值, 如果小于最小值则下标+1

    最大容量的下标获取原理同上, 判断最大容量下标所属内存大小是否大于最大值, 如果是则下标-1

     

     

    我们回到DefaultChannelConfig的构造方法:

    public DefaultChannelConfig(Channel channel) {
        this(channel, new AdaptiveRecvByteBufAllocator());
    }

    刚才我们剖析过了AdaptiveRecvByteBufAllocator()的创建过程, 我们继续跟到this()中:

    protected DefaultChannelConfig(Channel channel, RecvByteBufAllocator allocator) {
        setRecvByteBufAllocator(allocator, channel.metadata());
        this.channel = channel;
    }

    我们看到这里初始化了channel, 在channel初始化之前, 调用了setRecvByteBufAllocator(allocator, channel.metadata())方法, 顾名思义, 这是用于设置缓冲区分配器的方法, 第一个参数是我们刚刚分析过的新建的AdaptiveRecvByteBufAllocator对象, 第二个传入的是与channel绑定的ChannelMetadata对象, ChannelMetadata对象是什么?

    我们跟进到metadata()方法当中, 由于是channel是NioServerSocketChannel, 所以调用到了NioServerSocketChannel的metadata()方法:

    public ChannelMetadata metadata() {
        return METADATA;
    }

    这里返回了一个成员变量METADATA, 跟到这个成员变量中:

    private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);

     

    这里创建了一个ChannelMetadata对象, 并在构造方法中传入false和16

    继续跟到ChannelMetadata的构造方法中:

    public ChannelMetadata(boolean hasDisconnect, int defaultMaxMessagesPerRead) {
        //省略验证代码
        //false
        this.hasDisconnect = hasDisconnect;
        //16
        this.defaultMaxMessagesPerRead = defaultMaxMessagesPerRead;
    }

    这里做的事情非常简单, 只初始化了两个属性:

    hasDisconnect=false

    defaultMaxMessagesPerRead=16

    defaultMaxMessagesPerRead=16代表在读取对方的链接或者channel的字节流时(无论server还是client), 最多只循环16次, 后面的讲解将会看到

    剖析完了ChannelMetadata对象的创建, 我们回到DefaultChannelConfig的构造方法:

    protected DefaultChannelConfig(Channel channel, RecvByteBufAllocator allocator) {
        setRecvByteBufAllocator(allocator, channel.metadata());
        this.channel = channel;
    }

    跟到setRecvByteBufAllocator(allocator, channel.metadata())方法中:

    private void setRecvByteBufAllocator(RecvByteBufAllocator allocator, ChannelMetadata metadata) {
        if (allocator instanceof MaxMessagesRecvByteBufAllocator) {
            ((MaxMessagesRecvByteBufAllocator) allocator).maxMessagesPerRead(metadata.defaultMaxMessagesPerRead());
        } else if (allocator == null) {
            throw new NullPointerException("allocator");
        }
        rcvBufAllocator = allocator;
    }

    首先会判断传入的缓冲区分配器是不是MaxMessagesRecvByteBufAllocator类型的, 因为AdaptiveRecvByteBufAllocator实现了MaxMessagesRecvByteBufAllocator接口, 所以此条件成立

    之后将其转换成MaxMessagesRecvByteBufAllocator类型, 然后调用其maxMessagesPerRead(metadata.defaultMaxMessagesPerRead())方法, 这里会走到其子类DefaultMaxMessagesRecvByteBufAllocator的maxMessagesPerRead(int maxMessagesPerRead)方法中, 其中参数metadata.defaultMaxMessagesPerRead()返回就是ChannelMetadata的属性defaultMaxMessagesPerRead, 也就是16

    跟到maxMessagesPerRead(int maxMessagesPerRead)方法中:

    public MaxMessagesRecvByteBufAllocator maxMessagesPerRead(int maxMessagesPerRead) {
        //忽略验证代码
    
        //初始化为16
        this.maxMessagesPerRead = maxMessagesPerRead;
        return this;
    }

    这里将自身属性maxMessagesPerRead设置为16, 然后返回自身

    回到DefaultChannelConfig的构造方法:

    private void setRecvByteBufAllocator(RecvByteBufAllocator allocator, ChannelMetadata metadata) {
        if (allocator instanceof MaxMessagesRecvByteBufAllocator) {
            ((MaxMessagesRecvByteBufAllocator) allocator).maxMessagesPerRead(metadata.defaultMaxMessagesPerRead());
        } else if (allocator == null) {
            throw new NullPointerException("allocator");
        }
        rcvBufAllocator = allocator;
    }

    设置完了内存分配器的maxMessagesPerRead属性, 最后将DefaultChannelConfig自身的成员变量rcvBufAllocator设置成我们初始化完毕的allocator对象

     

    至此, 有关channelConfig有关的初始化过程剖析完成

     

    上一节: 执行任务队列

    下一节: 处理接入事件之handle的创建

     

  • 相关阅读:
    web前端的发展态势
    AngularJs 简单入门
    css代码优化篇
    git提交报错:Please make sure you have the correct access rights and the repository exists.
    Activiti工作流框架学习
    遍历map集合的4种方法
    js设置日期、月份增加减少
    Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
    webservice_rest接口_学习笔记
    相互匹配两个list集合+动态匹配${}参数
  • 原文地址:https://www.cnblogs.com/xiangnan6122/p/10203986.html
Copyright © 2011-2022 走看看