前言
在不久前,我学习了服务端启动Netty时大概做了哪些事,但在做那些事情之前,我们还有一个重要的组件要构造+启动,它就是NioEventLoop,本章就来学下NioEventLoop,顺便做下笔记。
本章分为以下三个小节(加上本节算四个):
- new ThreadPerTaskExecutor() - 创建NioEventLoop执行任务线程的线程执行器。
- newChild() - 创建NioEventLoop对象数组,并配置一些核心参数。
- chooserFactory.newChooser() - 线程选择器,负责给新连接绑定一个NioEventLoop
Netty Version: 4.1.6
先追下构造函数
一般在使用Netty时,我们都会看到如下两行代码:
// 负责接收新连接并抛给workerGroup
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
// 负责处理新连接
EventLoopGroup workerGroup = new NioEventLoopGroup();
在这里我就不贴完整代码了,因为根本用不上~
先进入new NioEventLoopGroup();看到如下代码:
public NioEventLoopGroup() {
this(0);
}
- 其中0就是默认设置的线程数,后面会变。
继续,进入this:
public NioEventLoopGroup(int nThreads) {
this(nThreads, (Executor) null);
}
- 传了一个null引用的Executor,另外这里补充一个知识点:null可以被强制类型转换成任意类型的对象。
继续,进入this:
public NioEventLoopGroup(
int nThreads, Executor executor, final SelectorProvider selectorProvider) {
this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);
}
继续,进入this:
public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,
final SelectStrategyFactory selectStrategyFactory) {
super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
}
继续,进入super【路标1】:
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
好了,俄罗斯套娃先暂停一下,顺便留个路标,因为这里有个有意思的参数:DEFAULT_EVENT_LOOP_THREADS,如果指定的线程数是0,则会将线程数替换成这个值。
那么这个值到底是多少呢?我们不妨跟进去看一下:
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
"io.netty.eventLoopThreads", Runtime.getRuntime().availableProcessors() * 2));
关于Runtime.getRuntime().availableProcessors()我在jdk8文档得到的解释是:返回cpu的核心数。
但我自己测试时,却发现有些不妥,我的cpu明明是四核,但是却返回8,运行结果如下图:
然后我在网上稍微查了下资料,然后再看下任务管理器的cpu参数,终于知道怎么回事了:
结果:我的cpu是4核8线程(逻辑处理器),而Runtime.getRuntime().availableProcessors()返回的是逻辑处理器的个数。
所以,如果我不指定线程数,Netty默认会设置2*逻辑处理器个数的线程数,在我的电脑上就是16个线程。
虽然我没有过线程数调优的经验,但是上面讲到Runtime.getRuntime().availableProcessors()的"坑"也还是要注意的,自己电脑跑没啥问题,可万一是线上环境可能就需要注意了。
同时,线程数设置为:2*逻辑处理器个数的线程数 也算是比较常见的参考数值了,之前在mysql的文档中也见过。
好了,中途追查了一下availableProcessors()的"bad case"后,我们重新回到俄罗斯套娃中,也就是上面的【路标1】,为了我这种方便懒人,下面再贴下坐标:
io.netty.channel.MultithreadEventLoopGroup#MultithreadEventLoopGroup(int, java.util.concurrent.Executor, java.lang.Object...)
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
继续,进入super:
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
}
继续,进入this,我们就能看到本章的三个主角了:
io.netty.util.concurrent.MultithreadEventExecutorGroup#MultithreadEventExecutorGroup(int, Executor, EventExecutorChooserFactory, Object...)
上面这个构造函数可以说是所有小节的起点了,如果在后面的小节中忘记是如何进到这个构造函数的,也可以翻阅此篇。
由于我不想将一篇博客篇幅拉太长,关于new ThreadPerTaskExecutor、newChild、chooserFactory.newChooser我就放到后面一小节记录一个。
小结
- Netty在设置线程数时,默认设置的是:逻辑处理器*2
- 构造函数的主要逻辑:创建NioEventLoop线程的线程执行器、创建NioEventLoop对象数组、创建线程选择器。