========= 本系统文章都是依据Netty的4.1.44.Final-SNAPSHOT这个版本来讲解====================
本文中关联的所有文章的总目录可以参看:系列文章目录
在前面一章中已经简单的了解了一个socket的代码结构,用了哪些类?那么我们就先了解下使用到的NioEventLoopGroup这个类
1.类的继承关系图
可以看出该类本身实际上是一个可以定时执行任务的线程池。我们在创建ServerBootstrap 类时一般设置父的EventLoopGroup 接收连接的池组时,都是使用的NioEventLoopGroup类,而处理连接后的数据处理的也是NioEventLoopGroup类。而具体该类的作用我们会分为几步来查看业务:
第一步就是创建该类时所做的业务;
第二步就是在ServerBootstrap 类在bind()时会调用register方法
2.创建类时代码执行的逻辑
2.1 序列图
首先通过序列图我们大致了解下NioEventGroup类主要做些什么?
2.1.1 具体说明其中一些步骤的内容
-
EventExecutor[] 数组
在第17步中事件执行选择工厂类在创建选择器时,使用了两种不同的选择方式,与创建的EventExecutor数组的长度有关(在上图中第8步创建的默认的数组长度),而这个长度是由使用者来决定的,一般有两种方式;
1) 当我们在创建NioEventLoopGroup类是传入了线程个数,那么就是以这个线程个数为准(线程个数必须要大于0 );
2)如果没有传入时系统会默认设置; -
后面我们看下默认设置的方式是:
如果使用的是空的构建方法或者设置的值是0时
new NioEventLoopGroup();
new NioEventLoopGroup(0);
NioEventLoopGroup 在其父类MultithreadEventLoopGroup 中会对线程个数进行设置;设置EventExecutor数组个数值的代码如下所示:
其中SystemPropertyUtil.getInt的处理逻辑就是
首先它会通过**System.getProperty(“io.netty.eventLoopThreads”)**从系统变量中取,如果取不到,然后默认为NettyRuntime.availableProcessors() * 2 个线程数
而NettyRuntime.availableProcessors()的处理它实际调用的是 :
SystemPropertyUtil.getInt(“io.netty.availableProcessors”,Runtime.getRuntime().availableProcessors());
方法,到此我们理清了这个逻辑:
1)首先如果用户设置了io.netty.eventLoopThreads 系统变量值,那么就以该值为准(如果该值大于1);
2)没有设置,再看用户是否设置了io.netty.availableProcessors系统变量值,如果设置了就以该值为准;如果没有设置那么就以当前服务器的处理器核数为准;
- 回过头来,我们再看第17步中的EventExecutor选择器的两种处理方式:
EventExecutorChooserFactory.EventExecutorChooser 实际是DefaultEventExecutorChooseFactory来实现的,而它内部创建这个对象的具体操作逻辑是:
1)当是2的指数次幂时(具体的算法可以参看我写的学习点中: Netty源码中知识点学习记录----2的指数次幂),选择下一个EventExecutor的处理类是PowerOfTwoEventExecutorChooser,它的处理方式如下所示:
private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
private final AtomicInteger idx = new AtomicInteger();
private final EventExecutor[] executors;
PowerOfTwoEventExecutorChooser(EventExecutor[] executors) {
this.executors = executors;
}
@Override
public EventExecutor next() {
return executors[idx.getAndIncrement() & executors.length - 1];
}
}
也就是说当EventExecutor的数组长度是2的指数次幂时,每到数组的长度换一个EventExecutor对象。
2)当不是2的指数次幂时,选择下一个EventExecutor的处理类是GenericEventExecutorChooser,它的处理方式如下所示:
private static final class GenericEventExecutorChooser implements EventExecutorChooser {
private final AtomicInteger idx = new AtomicInteger();
private final EventExecutor[] executors;
GenericEventExecutorChooser(EventExecutor[] executors) {
this.executors = executors;
}
@Override
public EventExecutor next() {
return executors[Math.abs(idx.getAndIncrement() % executors.length)];
}
}
因为idx AtomicInteger,它内部存储的实际上就是一个int数值, 所以上面两种类处理方式的唯一区别就是当idx的值超过int范围值进入负数时,PowerOfTwoEventExecutorChooser 类还是每增长一位从数组的下标0开始,然后到[数组长度-1]这样循环,而GenericEventExecutorChooser类当到int最小值时,它是从[数组长度-1]取,然后下标依次递减,这样重复执行;
- 通过序列图分析出该类中具体的属性赋值情况如下所示:
类名 | 属性类型 | 属性名 | 赋值 |
---|---|---|---|
MultithreadEventLoopGroup | int | DEFAULT_EVENT_LOOP_THREADS | 默认是处理器的2倍 |
MultithreadEventExecutorGroup | EventExecutor[] | children | 数组中放入的都是NioEventLoop类 |
MultithreadEventExecutorGroup | Set | readonlyChildren | 放入的就是EventExecutor[]中存储的元素,用UnmodifiableSet这个只读的集合重新封装了下 |
MultithreadEventExecutorGroup | EventExecutorChooserFactory.EventExecutorChooser | chooser | DefaultEventExecutorChooserFactory来创建的,根据children数组的长度有关 |
==============================================================================
后续文章还在编写中:下一篇是 Netty 源码分析一ServerBootstrap的bind方法
讲解这个方法是可以引出来NioEventLoopGroup在ServerBootstrap这个类中做了哪些事?
文章讲解的过程中,我们可以看到这些工具类:
SystemPropertyUtil----- 主要是对System.getProperty 进行封装
NettyRuntime -------主要是对Runtime.getRuntime().availableProcessors() 进行包装处理