netty源码学习笔记之NioEventLoopGroup初始化分析

NioEventLoop

首先我们先来了解一下NioEventLoop

NioEventLoopGroup就是一组NioEventLoop,而NioEventLoop又继承了Executor。因此,NioEventLoop本身实际上就是一个Executor

我们可以在源码中找到它的 execute() 方法

public void execute(Runnable task) {
    if (task == null) {
        throw new NullPointerException("task");
    }

    boolean inEventLoop = inEventLoop();
    if (inEventLoop) {
        addTask(task);
    } else {
        startThread();
        addTask(task);
        if (isShutdown() && removeTask(task)) {
            reject();
        }
    }

    if (!addTaskWakesUp && wakesUpForTask(task)) {
        wakeup(inEventLoop);
    }
}

该方法存在于其父类SingleThreadEventExecutor,并且其代码根据netty的版本会有所不同。

注意看上面的代码,其中有一句startThread();,而跟进去之后最终会跟到doStartThread() 方法里

private void doStartThread() {
    assert thread == null;
    executor.execute(...省略...);
}

这里我们发现一个executor成员变量,也就意味着,不仅NioEventLoop是一个Executor,并且它里面也存在着一个Executor。因此,NioEventLoop有两个execute() 方法。

NioEventLoopGroup

NioEventLoopGroup也是继承了Executor。因此,NioEventLoopGroup也是一个Executor,并且NioEventLoopGroup内部也有一个成员变量executor。因此,NioEventLoopGroup也有两个execute() 方法。

NioEventLoopGroup初始化

这里我们从无参构造开始,一路往下跟(具体就省略了),直接跟到这个方法:

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
    if (executor == null) {
        // 这个executor是group的executor,将来会为每个eventLoop创建一个线程
        executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
    }

    children = new EventExecutor[nThreads];

    for (int i = 0; i < nThreads; i ++) {
        boolean success = false;
        try {
            children[i] = newChild(executor, args);
            success = true;
        } catch (Exception e) {
            // TODO: Think about if this is a good exception type
            throw new IllegalStateException("failed to create a child event loop", e);
        } finally {
            //如果出现异常则会关闭掉所有已经创建好的eventLoop,并终止所有eventLoop上执行的任务
        }
    }
	//创建选择器
    chooser = chooserFactory.newChooser(children);
	//下面还有很多代码,但是不重点分析了
}

为了方便阅读,我把本次分析不涉及到的代码先删掉了

首先看一下这个构造器涉及到的几个参数(从无参构造开始):

  • nThreads : 线程数量,默认为cpu逻辑核心数两倍
  • executor : 这个group所包含的executor
  • selectorProvider : 用于提供selectorselectablechannel
  • selectStrategyFactory : 选择策略工厂实例

逐行分析

executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());

executor为空时,会先创建默认线程工厂,然后再创建线程executor对象。
这里的 executorNioEventLoopGroup 里的 Executor ,并不是指自己。

children = new EventExecutor[nThreads];

接下来创建了一个children数组,长度为线程数量。
因为NioEventLoopGroup是一组NioEventLoop,因此这里的children实际上是一组NioEventLoop

接下来跟进newChild() 方法:

children[i] = newChild(executor, args);
=========================================================================
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
	//这里将args参数强转了
    return new NioEventLoop(this, executor, (SelectorProvider) args[0],
            ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
}
=========================================================================
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
             SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
    super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
    provider = selectorProvider;
    // 创建一个selector的二元组
    final SelectorTuple selectorTuple = openSelector();
    selector = selectorTuple.selector;
    unwrappedSelector = selectorTuple.unwrappedSelector;
    selectStrategy = strategy;
}

首先看父类构造:

super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
========================================================================
protected SingleThreadEventLoop(EventLoopGroup parent, Executor executor,
                                    boolean addTaskWakesUp, int maxPendingTasks,
                                    RejectedExecutionHandler rejectedExecutionHandler) {
    super(parent, executor, addTaskWakesUp, maxPendingTasks, rejectedExecutionHandler);
    //创建一个收尾队列
    tailTasks = newTaskQueue(maxPendingTasks);
}
========================================================================
protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
                                        boolean addTaskWakesUp, Queue<Runnable> taskQueue,
                                        RejectedExecutionHandler rejectedHandler) {
    super(parent);
    this.addTaskWakesUp = addTaskWakesUp;
    this.maxPendingTasks = DEFAULT_MAX_PENDING_EXECUTOR_TASKS;
    this.executor = ThreadExecutorMap.apply(executor, this);
    //创建一个任务队列
    this.taskQueue = ObjectUtil.checkNotNull(taskQueue, "taskQueue");
    rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
}

这里重点关注这行代码:

this.executor = ThreadExecutorMap.apply(executor, this);

这里this.executor是当前eventLoop里面的executor,而作为参数里面的executor则是前面 NioEventLoopGroup 构造方法一直传过来的的默认 Executor,我们继续往下看,看这个eventLoop里面的executor是怎么完成初始化的:

public static Executor apply(final Executor executor, final EventExecutor eventExecutor) {
	//这个executor就是eventLoop的executor
    return new Executor() {
        @Override
        public void execute(final Runnable command) {
            executor.execute(apply(command, eventExecutor));
        }
    };
}
=========================================================================
public static Runnable apply(final Runnable command, final EventExecutor eventExecutor) {
    return new Runnable() {
        @Override
        public void run() {
            setCurrentEventExecutor(eventExecutor);
            try {
            	//注意一下,这里是run方法
                command.run();
            } finally {
                setCurrentEventExecutor(null);
            }
        }
    };
}

从这里可以看出,eventLoop创建executor实际上就是再调用传过来的executor的方法(也就是NioEventLoopGroupexecutor成员的executor方法,就是默认的executor方法),也就是说,这里执行的executor里的代码实际上是NioEventLoopGroupexecutor成员的executor方法。
并且在执行的时候先会记录当前线程信息,并且在执行完成后删掉。

回到newChild方法,看openSelector()

先来看一个内部类SelectorTuple

private static final class SelectorTuple {
    final Selector unwrappedSelector;
    final Selector selector;

    SelectorTuple(Selector unwrappedSelector) {
        this.unwrappedSelector = unwrappedSelector;
        this.selector = unwrappedSelector;
    }

    SelectorTuple(Selector unwrappedSelector, Selector selector) {
        this.unwrappedSelector = unwrappedSelector;
        this.selector = selector;
    }
}

可以看到,SelectorTuple类里面有两个selector,用于封装优化前和优化后的selector,而openSelector() 方法就是根据配置判断是否优化当前的selector

private SelectorTuple openSelector() {
        final Selector unwrappedSelector;
        //根据provider创建出一个nio原生selector
        unwrappedSelector = provider.openSelector();
        //如果禁用了keySet优化则直接返回原生的selector
        if (DISABLE_KEY_SET_OPTIMIZATION) {
            return new SelectorTuple(unwrappedSelector);
        }

        Object maybeSelectorImplClass = AccessController.doPrivileged(new PrivilegedAction<Object>() {
        	//具体的优化过程   
        }
    }

这里只需知道,openSelector() 方法先是创建一个原生的selector,然后查看配置,若是禁用了keySet优化则直接就返回原生selector,否则尽心优化后再返回。

总结:

1.NioEventLoopGroupEventLoopGroup一样,既是一个executor,里面有execute方法,同时里面还包含着一个executor成员变量,也有一个execute方法。
2. 首先根据指定线程数创建相应数量的eventLoop,默认为逻辑核心数的2倍。
3. 创建 eventLoop 主要是先创建一个收尾队列和一个任务队列,然后将默认的 executor 传给 eventLoopexecutor 并让其运行,然后根据配置判断需不需要优化创建的 selector

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值