NioEventLoopGroup的初始化
文章目录
源码版本4.1.66
类图
从继承关系和线程组的作用来猜想:
- NioEventLoopGroup作为Executor的子类将会提供任务执行的方法(execute)
- 而作为ExecutorService的子类,它肯定也会为我们提供异步执行任务的方法(submit)
- 作为SchedualExecutorService的子类,它肯定也支持定时任务调度(schedule)
- NioEventLoopGroup作为一个线程组,当我们提交任务给它执行时,涉及到线程组中的线程选择,那么也肯定涉及到相应的选择器(next方法)
初始化过程
通常在创建服务端ServerBootstrap时,通常会初始化boss和worker两个线程组,而一般使用的就是NioEventLoopGroup的构造方法进行创建:
// 无参构造
public NioEventLoopGroup() {
this(0);
}
// nThread 默认创建的线程数量
public NioEventLoopGroup(int nThreads) {
this(nThreads, (Executor) null);
}
我们可以通过指定线程数量创建指定NioEventLoop数量的NioEventLoopGroup(也可以使用无参构造,不指定数量),它会调用父类MultithreadEventLoopGroup的构造方法进行初始化:
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
// 再调用父类 MultithreadEventExecutorGroup 的构造
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
MultithreadEventLoopGroup为NioEventLoopGroup提供多线程初始化时的部分实现,其中包括指定线程的数量:
- 当指定线程数量参数时,按指定的数量进行创建
- 当不指定线程数量参数时,则会默认使用CPU核心数*2的数量创建
接下来再调用父类 MultithreadEventExecutorGroup 的构造方法。
MultithreadEventExecutorGroup构造
该类中有两个参数需要特别注意:
private final EventExecutor[] children;
private final EventExecutorChooserFactory.EventExecutorChooser chooser;
- 第一个EventExecutor就是NioEventLoop的父类,children也就是该group对应的线程组。
- 第二个则是为选择线程提供策略的选择器
接下来一起继续看构造方法,上一步在MultithreadEventLoopGroup中指定了线程数量之后,调用父类的构造如下:
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
}
在这一步会传入一个参数 DefaultEventExecutorChooserFactory.INSTANCE,它的作用是:当需要从线程组中选出一个元素执行任务时,为我们提供选择策略工厂,为接下来初始化选择器做准备。
接下来,就到了关键的地方,这里先把代码贴出来,然后一步步进行分析:
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
checkPositive(nThreads, "nThreads");// 校验线程数量的合法性
// 第一步:初始化ThreadPerTaskExecutor
if (executor == null) {
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 {
if (!success) {
// 初始化失败要做的事,省略...
}
}
}
// 第三步:初始化选择器
chooser = chooserFactory.newChooser(children);
// 添加监听
final FutureListener<Object> terminationListener = new FutureListener<Object>() {
@Override
public void operationComplete(Future<Object> future) throws Exception {
if (terminatedChildren.incrementAndGet() == children.length) {
terminationFuture.setSuccess(null);
}
}
};
for (EventExecutor e: children) {
e.terminationFuture().addListener(terminationListener);
}
Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
Collections.addAll(childrenSet, children);
readonlyChildren = Collections.unmodifiableSet(childrenSet);
}
其初始化大致分为以下三步:
- 初始化任务执行器executor
- 初始化线程组children
- 初始化线程选择器chooser
接下来一步步看,首先是ThreadPerTaskExecutor的初始化。
初始化任务执行器executor
任务执行器的类型是ThreadPerTaskExecutor,通过查看它的类图可以看到它是Executor的子类,内部只提供了包含线程工厂的构造方法和执行任务的execute方法:
在上一步初始化ThreadPerTaskExecutor的过程中,传入netty提供的默认的线程工厂DefaultThreadFactory:
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
execute()方法
ThreadPerTaskExecutor这个执行器通过execute()方法执行任务时的代码如下:
@Override
public void execute(Runnable command) {
threadFactory.newThread(command).start();
}
从这里可以得知:在以后的任务执行中,将会调用DefaultThreadFactory的newThread方法获取一个线程,然后调用线程的start方法异步执行该任务。
DefaultThreadFactory.newThread()
@Override
public Thread newThread(Runnable r) {
// 将runnable包装成一个FastThreadLocalRunnable
Thread t = newThread(FastThreadLocalRunnable.wrap(r), prefix + nextId.incrementAndGet());
try {
if (t.isDaemon() != daemon) {
t.setDaemon(daemon);
}
if (t.getPriority() != priority) {
t.setPriority(priority);
}
} catch (Exception ignored) {
// Doesn't matter even if failed to set.
}
return t;
}
// 新建一个FastThreadLocalThread
protected Thread newThread(Runnable r, String name) {
return new FastThreadLocalThread(threadGroup, r, name);
}
该方法主要分为以下两步:
- 将任务Runnable包装成一个FastThreadLocalRunnable
- 新建一个Thread子类FastThreadLocalThread
FastThreadLocalRunnable
FastThreadLocalRunnable是对Runnable的一层包装,内部重写了run()方法,在任务执行完毕之后添加了对线程变量FastThreadLocal的remove操作:
@Override
public void run() {
try {
runnable.run();
} finally {
FastThreadLocal.removeAll();
}
}
FastThreadLocalThread
FastThreadLocalThread作为线程Thread的子类,构造方法中完成了线程的初始化操作:
public FastThreadLocalThread(ThreadGroup group, Runnable target, String name) {
super(group, FastThreadLocalRunnable.wrap(target), name);
cleanupFastThreadLocals = true;
}
至于FastThreadLocalThread及FastThreadLocal的优点我们以后再详细探究…
关于ThreadPerTaskExecutor的作用就介绍到这里,接下来一起看这个executor将会在下面发挥什么作用。
初始化线程组children
先把代码片段贴出来:
children = new EventExecutor[nThreads];// EventExecutor数组
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
children[i] = newChild(executor, args);
success = true;
} catch (Exception e) {
//...
} finally {
if (!success) {
// 执行失败要做的事,省略...
}
}
}
不难看出这里将会通过循环的方式初始化数组children,newChild方法会把刚才创建的 ThreadPerTaskExecutor 当作参数传入。
这里就不难想到:该任务执行器会为NioEventLoop执行任务提供帮助(创建FastThreadLocalThread并执行任务)。
接下来一起看一下newChild,newChild方法是 MultithreadEventExecutorGroup 提供的一个模板方法,在这里由子类 NioEventLoopGroup 进行实现:
protected abstract EventExecutor newChild(Executor executor, Object... args) throws Exception;
// NioEventLoopGroup 的实现
@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
SelectorProvider selectorProvider = (SelectorProvider) args[0];
SelectStrategyFactory selectStrategyFactory = (SelectStrategyFactory) args[1];
RejectedExecutionHandler rejectedExecutionHandler = (RejectedExecutionHandler) args[2];
EventLoopTaskQueueFactory taskQueueFactory = null;
EventLoopTaskQueueFactory tailTaskQueueFactory = null;
int argsLength = args.length;
if (argsLength > 3) {
taskQueueFactory = (EventLoopTaskQueueFactory) args[3];
}
if (argsLength > 4) {
tailTaskQueueFactory = (EventLoopTaskQueueFactory) args[4];
}
return new NioEventLoop(this, executor, selectorProvider,
selectStrategyFactory.newSelectStrategy(),
rejectedExecutionHandler, taskQueueFactory, tailTaskQueueFactory);
}
这里暂时先把关注点放在最后一行:初始化NioEventLoop。
NioEventLoop初始化
先来看一下NioEventLoop的类关系图:
然后再来看它的构造方法:
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler,
EventLoopTaskQueueFactory taskQueueFactory, EventLoopTaskQueueFactory tailTaskQueueFactory) {
// 调用父类构造
super(parent, executor, false, newTaskQueue(taskQueueFactory),
newTaskQueue(tailTaskQueueFactory), rejectedExecutionHandler);
this.provider = ObjectUtil.checkNotNull(selectorProvider, "selectorProvider");
this.selectStrategy = ObjectUtil.checkNotNull(strategy, "selectStrategy");
// 初始化Selector
final SelectorTuple selectorTuple = openSelector();
this.selector = selectorTuple.selector;
this.unwrappedSelector = selectorTuple.unwrappedSelector;
}
接下来一步步看他的构造过程:
1,首先新建任务队列
newTaskQueue()方法最终将会创建一个多生产者单消费者模型的队列,这里我们后面再详细探究:
private static Queue<Runnable> newTaskQueue0(int maxPendingTasks) {
// This event loop never calls takeTask()
return maxPendingTasks == Integer.MAX_VALUE ? PlatformDependent.<Runnable>newMpscQueue()
: PlatformDependent.<Runnable>newMpscQueue(maxPendingTasks);
}
2,然后调用SingleThreadEventLoop构造
这里将继续调用父类 SingleThreadEventExecutor 的构造,然后把 tailTaskQueue 赋值给 SingleThreadEventLoop 的 tailTasks 属性上:
protected SingleThreadEventLoop(EventLoopGroup parent, Executor executor,
boolean addTaskWakesUp, Queue<Runnable> taskQueue, Queue<Runnable> tailTaskQueue,
RejectedExecutionHandler rejectedExecutionHandler) {
super(parent, executor, addTaskWakesUp, taskQueue, rejectedExecutionHandler);
tailTasks = ObjectUtil.checkNotNull(tailTaskQueue, "tailTaskQueue");
}
父类 SingleThreadEventExecutor 的构造主要目的是指定任务队列、执行器等参数,这里就不再继续下去了。
3,初始化Selector
回到NioEventLoop构造方法的这三行:
final SelectorTuple selectorTuple = openSelector();
this.selector = selectorTuple.selector;
this.unwrappedSelector = selectorTuple.unwrappedSelector;
openSelector() 用于初始化Selector:
- 获取到Java层面的Selector(java.nio.channels)
- 将该Selector包装成一个SelectedSelectionKeySetSelector
到这里NioEventLoop的初始化就结束了,了解Reactor模型的话,不难猜到后续NioEventLoop必然会作为一个线程去处理连接或读写请求,这里就涉及到客户端如何处理连接、读、写请求的内容,后面再详细探究。
初始化选择器chooser
这里通常情况下是创建一个GenericEventExecutorChooser来作为选择器,当我们需要选取一个组里的线程去执行任务时,将会通过该选择器的提供的next()策略来挑选:
@Override
public EventExecutor next() {
return executors[(int) Math.abs(idx.getAndIncrement() % executors.length)];
}
这里可以看到这里选取的方式就是针对当前索引%数组长度,说白了就是挨个取。
总结
到这里整个NioEventLoopGroup的初始化就大致介绍完成了,也算是对它有了一个比较初步的了解,后面我们再对本文涉及的一些内容如:FastThreadLocal、MpScQueue、ServerBootstrap的初始化等等内容进行详细探究。