本文主要分享Netty中事件循环机制的实现。
源码分析基于Netty 4.1
EventLoop
前面分享服务端和客户端启动过程的文章中说过,Netty通过事件循环机制(EventLoop)处理IO事件和异步任务,简单来说,就是通过一个死循环,不断处理当前已发生的IO事件和待处理的异步任务。示例如下
while(true) {
process(selector.select());
process(getTask());
}
这种事件循环机制也是一种常用的IO事件处理机制,包括Redis,Mysql都使用了类似的机制。
关于异步任务,前面文章说过,EventLoop实现了(jvm)Executor的接口,execute方法可以提供异步任务。
register,bind,connect等操作,都会提交一个任务给EventLoop处理。如
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
eventLoop.execute(new Runnable() {
public void run() {
register0(promise);
}
});
}
下面看一下Netty中事件循环机制相关的类。
EventExecutor,事件执行器,负责处理事件。
EventExecutorGroup维护了一个EventExecutor链表,它继承了ScheduledExecutorService,execute方法通过next方法选择一个EventExecutor,并调用EventLoop#execute处理事件。
(EventExecutor继承了EventExecutorGroup,可以看做一个特殊的EventExecutorGroup,其execute方法可以提交一个任务任务)
EventLoop,事件循环器,继承了EventExecutor,通过循环不断处理注册于其上的Channel的IO事件。
EventLoopGroup接口则继承了EventExecutorGroup,负责调度EventLoop。
SingleThreadEventExecutor实现了EventExecutor,它会创建一个新线程,并在该线程上处理事件,可以理解为单线程处理器。
MultithreadEventExecutorGroup实现EventExecutorGroup,可以理解为多线程处理器(实际上是维护了多个EventExecutor,一个EventExecutor可以理解为一个线程),newChild方法构造具体的EventExecutor。
MultithreadEventExecutorGroup可以配置EventExecutor数量,即线程数量。
EventExecutorChooserFactory.EventExecutorChooser负责选择一个EventExecutor执行实际操作。
NioEventLoop继承了SingleThreadEventExecutor,负责处理NIO事件。所以,一个NioEventLoop对象可以看做是一个线程。
NioEventLoop也实现了EventLoop接口,它实现了事件循环机制,是Netty核心类。
MultithreadEventLoopGroup继承了MultithreadEventExecutorGroup,并实现了EventLoopGroup,其newChild方法构造具体的EventLoop。
NioEventLoopGroup#newChild会构建NioEventLoop。
EventLoop各实现类关系如下
启动
SingleThreadEventExecutor关键字段
private final Queue<Runnable> taskQueue; // 待处理异步任务
private volatile Thread thread; // EventLoop执行线程,即SingleThreadEventExecutor创建的新线程
private final Executor executor; // java.util.concurrent.Executor,负责创建线程
当我们通过execute方法提交任务时,如果还没有创建执行新线程,会通过SingleThreadEventExecutor#executor一个新线程,并在新线程中调用run方法(run方法由子类实现,负责实现事件循环机制,新线程是EventLoop真正执行线程)。
SingleThreadEventExecutor#execute
public void execute(Runnable task) {
...
boolean inEventLoop = inEventLoop();
// #1
addTask(task);
// #2
if (!inEventLoop) {
startThread();
// #3
if (isShutdown()) {
...