回顾

上一节,我跟进了NioEventLoop大致的构造过程(newChild方法),并且知道创建出的NioEventLoop实例最终会保存成属性名为children的EventExecutor数组,而这个children到后面又被用于构造一个chooser,本节就来探究下这个chooser的构建过程。

另外,chooser的作用就是给新连接绑定NioEventLoop,这个在第一节也提到过。

原谅我又拿前面的MultithreadEventExecutorGroup构造函数图出来,因为方便定位起点,忘记的可以看第一节
MultithreadEventExecutorGroup.png

此处【坐标1】
MultithreadEventExecutorGroup构造函数.png

Netty Version:4.1.6


追踪chooserFactory.newChooser(children);

可能有人会问为什么不先理一理整个流程?因为这个流程是真的简单,以致于我原本都没想过要记录这个过程,直到我遇到一个能与HashMap起到一点共鸣的东西。

好了,废话不多说,视角回到【坐标1】,找到以下代码:

chooser = chooserFactory.newChooser(children);

进入newChooser方法,此处【坐标2】:
io.netty.util.concurrent.DefaultEventExecutorChooserFactory#newChooser

    public EventExecutorChooser newChooser(EventExecutor[] executors) {
        if (isPowerOfTwo(executors.length)) {
            return new PowerOfTowEventExecutorChooser(executors);
        } else {
            return new GenericEventExecutorChooser(executors);
        }
    }

先进入isPowerOfTwo方法看看:

    private static boolean isPowerOfTwo(int val) {
        return (val & -val) == val;
    }
  • 这个方法就如同它的名字,就是判断数字是否是2的幂次方,相信你只要了解过一点jdk8的HashMap优化,应该就能猜出【坐标2】的中的代码到底是干嘛了。

我们将视角切回【坐标2】,然后将视角放大到类的所有函数,发现PowerOfTowEventExecutorChooser和GenericEventExecutorChooser的构造函数都是做同一件事,就是将NioEventLoop数组(属性名:executors)保存到自己的属性中。

注意,上面我特意强调了是NioEventLoop,就是怕被executors这个属性名以及EventExecutor这个类名给骗了,在上一节中也讲了为什么是这样,如果还不信,我贴个断点图:

在这个类中,我们应该重点关注的是两个内部类的next()方法:
io.netty.util.concurrent.DefaultEventExecutorChooserFactory.GenericEventExecutorChooser#next

public EventExecutor next() {
            return executors[Math.abs(idx.getAndIncrement() % executors.length)];
        }

io.netty.util.concurrent.DefaultEventExecutorChooserFactory.PowerOfTowEventExecutorChooser#next

        public EventExecutor next() {
            return executors[idx.getAndIncrement() & executors.length - 1];
        }

先解释下,next方法就是负责给新连接分配NioEventLoop的。

我在前面说过的一个类HashMap优化的思想,就体现在PowerOfTowEventExecutorChooser的next方法,即如果executors的数组长度是2的幂次方,调用next方法时,就会使用位运算做取模运算,从而提升性能。


小结:

  • chooserFactory.newChooser(children);的主要逻辑就是创建一个选择器,并将先前创建NioEventLoop保存到自己的属性中,用于分发给新连接。
  • 分发的策略会根据NioEventLoop的个数,即根据我们设置的线程数优化,如果设置的数值是2的幂次方,可能会获得更好的性能。