NioEventLoop是什么?
如图,NioEventLoop是worker threads中的thread,也就是处理请求的线程,属于NioEventLoopGroup,那么多个线程每次选择哪个线程来处理请求呢?
1.1 Netty给Channel分配Nio Event Loop的规则
看下图,EventLoopGroup是线程组,每个EventLoop是一个线程,那么线程处理请求是怎么分配的呢?我们看一下源码
1.1.1 我们通过 MultithreadEventLoopGroup.register方法定位到next()方法。
该方法负责NioEventLoop。
1.1.2 进入super.next()方法,我们看到MultithreadEventExecutorGroup的属性chooser
点击chooser,我们找到它赋值的地方在构造方法里面,chooser的正是通过DefaultEventExecutorChooserFactory的newChooser获取的,
进入newChooser方法,根据传入的executors长度是否2的指数返回不同的实现类,该类实现了一个策略模式。
注意到这里isPowerOfTwo只有一行代码,为什么还有单独写一个方法呢,虽然只有一行代码但是不容易看懂,通过方法名我们一下就看懂了,这是一种可读性更好的实现方式
1.1.3 我们继续看chooser.next()方法,
有两个实现类GenericEventExecutorChooser和PowerOfTwoEventExecutorChooser,都是在DefaultEventExecutorChooserFactory中。
看两个实现类实现的有何不同
两者都是通过idx来获取索引值的,idx是递增的,这种模式类似轮询的方式。但是idx一直递增下去的话,可能会整数类型的越界,
为了防止越界两者做了不同的处理
- GenericEventExecutorChooser,idx对executors.length取模,并取绝对值
- PowerOfTwoEventExecutorChooser, idx对executors.length - 1做与运算确定位置,这种方式类似于HashMap中对桶位置运算的处理,与运算比数学运算快很多,但是只能在executors.length是2的指数的时候才可以发挥作用。
例如: executors.length = 2^4, 其二进制为10000,executors.length-1=01111。任何值与1111与运算都不会超过executors.length,同时任何小于executors.length的值和1111与运算都是其本身。这是这个特性才能实现这样的优化。
这里PowerOfTwoEventExecutorChooser对next方法做了优化,所以在长度是2的指数的时候我们可以更快的处理
1.1.4 总结
- 事件处理EventLoop的分配方式是轮询的,通过记录调用次数对EventLoop的数量取余来确定,这种方式能比较好的保证负载均衡
- 同时根据EventLoop数量是否2的指数做了优化
1.2 多路复用怎么跨平台的
在不同的系统平台上,EventLoop如何跨平台呢?我们看一下源码
1.2.1 我 们看一下new NioEventLoopGroup()
点进这个方法,继续进入,我们找到下面这个方法,看Selector来自SelectorProvider.provider
进入SelectorProvider.provider,这里的run方法里面有三个分支。
- 第一个是从配置的类加载provider
- 第二个是从系统加载provider
- 如果前两个都没有,看第三个sun.nio.ch.DefaultSelectorProvider.create,这个方法是jre虚拟机的方法,不同平台的jdk版本上,该方法不一样,
Selector通过这种方式来实现跨平台
下图是openjdk不同平台是Selector实现:
1.2.2 总结
我们可以看到跨平台是通过不同平台的jvm实现的,不同jvm的平台有不同的实现类,在不同的平台会调用jvm的实现类。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。