美文网首页
netty源码分析(三) - NioEventLoop - 1创

netty源码分析(三) - NioEventLoop - 1创

作者: 进击的蚂蚁zzzliu | 来源:发表于2020-10-08 17:34 被阅读0次

    概述

    创建过程主要有以下4步:

    1. 创建NioEventLoopGroup
    2. 创建ThreadPerTaskExecutor
    3. 创建NioEventLoop
    4. 创建chooser
      下面对这四个步骤进行详细解析

    1. 创建NioEventLoopGroup

    NioEventLoopGroup创建.png

    如上图构造器的调用过程,其中重点关注几个参数的默认值

    • selectorProvider:根据操作系统返回不同的provider
    • selectStrategyFactory:默认DefaultSelectStrategyFactory,后续run()中select时会用到(有task就通过selectNow取,没有返回-1)
    • nThread:NioEventLoop数组size(也就是线程数)为空时默认为当前cpu核数的两倍
    • chooserFactory:默认DefaultEventExecutorChooserFactory,后续通过该factory获取选择器(从NioEventLoop数组中选择一个跟新连接进行绑定)
    • args可变参数
      第一个参数selectorProvider;
      第二个参数selectStrategyFactory
      第三个参数(拒绝策略)默认RejectedExecutionHandler(抛出
      RejectedExecutionException异常)

    2/3/4步代码如下(忽略了非主流程代码)

    protected MultithreadEventExecutorGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) {
        if (executor == null) {
            //2. 创建ThreadPerTaskExecutor
            executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
        }
        //初始化大小为nThreads数组(NioEventLoop数组)
        children = new EventExecutor[nThreads];
        for (int i = 0; i < nThreads; i ++) {
            boolean success = false;
            try {
                //3. 创建NioEventLoop
                children[i] = newChild(executor, args);
                success = true;
            } catch (Exception e) {
                throw new IllegalStateException("failed to create a child event loop", e);
            } finally {
                //忽略
            }
        }
        //4. 创建chooser
        chooser = chooserFactory.newChooser(children);
    }
    

    2. 创建ThreadPerTaskExecutor

    ThreadPerTaskExecutor 每次执行任务都会创建一个线程实体; 通过ThreadFactory进行构造,然后每次execute都会创建一个线程去运行

    public final class ThreadPerTaskExecutor implements Executor {
        private final ThreadFactory threadFactory;
        public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
            //构造方法传入threadFactory 即 newDefaultThreadFactory()返回
            this.threadFactory = ObjectUtil.checkNotNull(threadFactory, "threadFactory");
        }
        @Override
        public void execute(Runnable command) {
            //该执行器execute时,即通过threadFactory创建一个thread,然后start
            threadFactory.newThread(command).start();
        }
    }
    

    newDefaultThreadFactory(),线程工厂,通过newThread创建线程

    public DefaultThreadFactory(String poolName, boolean daemon, int priority, ThreadGroup threadGroup) {
        //默认线程名:nioEventLoopGroup-group序号(从1自增)-线程号(从0自增)
        prefix = poolName + '-' + poolId.incrementAndGet() + '-';
        //是否守护线程:false
        this.daemon = daemon;
        //优先级:10
        this.priority = priority;
        this.threadGroup = threadGroup;
    }
    
    protected Thread newThread(Runnable r, String name) {
        //创建FastThreadLocalThread
        return new FastThreadLocalThread(threadGroup, r, name);
    }
    
    • ThreadPerTaskExecutor:执行器实现jdk Executor接口
    • DefaultThreadFactory:创建执行线程的工厂,创建出FastThreadLocalThread,FastThreadLocalThread继承自jdk Thread,netty为了提高threadLocal性能设计,后续单独详解

    3. 创建NioEventLoop

    创建NioEventLoop放到数组中,新连接建立时会从数组中选取一个NioEventLoop进行绑定

    NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
                     SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler,
                     EventLoopTaskQueueFactory queueFactory) {
            //newTaskQueue创建MpscQueue(一个消费者多个生产者的队列)
            super(parent, executor, false, newTaskQueue(queueFactory), newTaskQueue(queueFactory), rejectedExecutionHandler);
            //SelectorProvider.provider()
            this.provider = ObjectUtil.checkNotNull(selectorProvider, "selectorProvider");
            //DefaultSelectStrategy
            this.selectStrategy = ObjectUtil.checkNotNull(strategy, "selectStrategy");
            //调用jdk openSelector方法打开一个选择器selector
            final SelectorTuple selectorTuple = openSelector();
            this.selector = selectorTuple.selector;
            this.unwrappedSelector = selectorTuple.unwrappedSelector;
        }
    
    • parent:NioEventLoopGroup
    • executor: ThreadPerTaskExecutor,第二步创建
    • selectorProvider:SelectorProvider.provider(),第一步初始化
    • strategy:DefaultSelectStrategy,第一步初始化
    • rejectedExecutionHandler:RejectedExecutionHandlers,第一步初始化
    • queueFactory:默认null
    • newTaskQueue:创建MpscQueue队列(后续详细讲解)

    4. 创建chooser

    后续新连接通过该选择器找到需要绑定的NioEventLoop

    public EventExecutorChooser newChooser(EventExecutor[] executors) {
        //nThread是否 2 的倍数
        if (isPowerOfTwo(executors.length)) {
            //经过优化的方式获取数组下标:idx.getAndIncrement() & executors.length - 1
            return new PowerOfTwoEventExecutorChooser(executors);
        } else {
            //普通方式获取数组下标:Math.abs(idx.getAndIncrement() % executors.length)
            return new GenericEventExecutorChooser(executors);
        }
    }
    
    • isPowerOfTwo:数组长度是否2的倍数
    • 是2的倍数:会通过优化的方式获取下标(通过位运算),性能更好;
    • 不是2倍数:普通方式获取下标(直接取模)
      两种方式最终达到的效果都是顺序依次获取,只不过2的倍数时可以通过位运算提升性能;

    相关文章

      网友评论

          本文标题:netty源码分析(三) - NioEventLoop - 1创

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