在查看源码前,先了解下Netty中的线程池EventLoopGroup
是如何执行任务的,因为源码中很多异步操作都是把任务提交到EventLoopGroup
中。
EventLoopGroup
可以理解为一个线程池,以NioEventLoopGroup
为例查看继承体系。
Iterable
,可以通过迭代的方式查看里面的元素。Executor
,线程池的顶级接口,包含一个execute()
方法,用于提交任务到线程池中。ExecutorService
,继承了Executor
接口,并新增了一些方法,如submit()
方法、shutdown()
方法。ScheduledExecutorService
,继承了ExecutorService
接口,增加了定时任务相关的方法。上面四个接口,后面三个类都是线程池相关的接口。
EventExecutorGroup
,继承了ScheduledExecutorService
,提供了next()
方法用于获取一个EventExecutor
。EventLoopGroup
,扩展自 EventExecutorGroup
,并增加或修改了两大功能,一是提供了 next()
方法用于获取一个 EventLoop
,二是提供了注册 Channel
到事件轮询器中。MultithreadEventLoopGroup
,抽象类,EventLoopGroup
的所有实现类都继承自这个类,可以看作是一种模板,从名字也可以看出来它里面包含多个线程来处理任务。NioEventLoopGroup
,具体实现类,使用 NIO 形式(多路复用中的 select)工作的 EventLoopGroup
。更换前缀就可以得到不同的实现类,比如 EpollEventLoopGroup
专门用于 Linux 平台,KQueueEventLoopGroup
专门用于 MacOS/BSD 平台。EventLoop
可以理解为是 EventLoopGroup
中的工作线程,类似于Java
线程池中的工作线程,它里面包含了一个线程,控制着这个线程的生命周期。
以 NioEventLoop
为例看看它的继承体系:
EventExecutor
,扩展自 EventLoopGroup
,主要增加了判断一个线程是不是在 EventLoop
中的方法。
OrderedEventExecutor
,扩展自 EventExecutor
,这是一个标记接口,标志着里面的任务都是按顺序执行的。
EventLoop
,扩展自 EventLoopGroup
,它将为已注册进来的 Channel
处理所有的 IO 事件,另外,它还扩展自 OrderedEventExecutor
接口,说明里面的任务是按顺序执行的。
SingleThreadEventLoop
,抽象类,EventLoop
的所有实现类都继承自这个类,可以看作是一种模板,从名字也可以看出来它是使用单线程处理的。
NioEventLoop
,具体实现类,绑定到一个 Selector
上,同时可以注册多个 Channel
到 Selector
上,同时,它继承自 SingleThreadEventLoop
,也就说明了一个 Selector
对应一个线程。同样地,更换前缀就可以得到不同的实现,比如 EpollEventLoop
、KQueueEventLoop
。
以NioEventLoopGroup
为例,通过源码分析EventLoopGroup
是如何执行任务的。
public class Main {
public static void main(String[] args) throws IOException {
EventLoopGroup bossGroup = new NioEventLoopGroup(4);
// 这里的next()方法返回的是NioEventLoop实例
// execute()方法提交任务
bossGroup.next().execute(() -> {
System.out.println(Thread.currentThread() + ":" + LocalDateTime.now().toString());
});
System.in.read();
}
}
查看NioEventLoopGroup
的构造方法,最后会进入到父类MultithreadEventExecutorGroup
的构造方法。
public abstract class MultithreadEventExecutorGroup extends AbstractEventExecutorGroup {
// 这里的具体实现就是 NioEventLoop
private final EventExecutor[] children;
// EventExecutor 选择器, 通过next()方法从 children 列表里面选择一个 EventExecutor 执行任务
private final EventExecutorChooserFactory.EventExecutorChooser chooser;
// 构造方法
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
// 线程数量必须大于0
if (nThreads <= 0) {
throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
}
// 这里的executor是用来创建线程的
if (executor == null) {
// newDefaultThreadFactory()返回的是io.netty.util.concurrent.DefaultThreadFactory,继承自 java.util.concurrent.ThreadFactory,
// NioEventLoop 里面的线程(FastThreadLocalThread)就是通过这个生成的
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory()); // ①
}
// 创建数组
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
// newChild 方法由子类 NioEventLoopGroup 实现
// 返回一个 NioEventLoop
children[i] = newChild(executor, args); // ②
} 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 {
// 省略......
}
}
/**
* 线程选择器
* 如果线程数量是2的N次方, 则使用:PowerOfTwoEventExecutorChooser, 否则使用GenericEventExecutorChooser
* chooser是用来从children里面选择EventExecutor(这里的具体实现是NioEventLoop), 前者做了优化, 更加高效
* 前者: executors[idx.getAndIncrement() & executors.length - 1]
* 后者: executors[(int) Math.abs(idx.getAndIncrement() % executors.length)]
*/
chooser = chooserFactory.newChooser(children);
// 省略......
}
}
①ThreadPerTaskExecutor
是用来创建线程的,通过execute
方法创建线程并执行任务,这里面创建的线程是Netty实现的FastThreadLocalThread
。
public final class ThreadPerTaskExecutor implements Executor {
// 线程工厂
private final ThreadFactory threadFactory;
// 构造函数传线程工厂
public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
this.threadFactory = ObjectUtil.checkNotNull(threadFactory, "threadFactory");
}
// 新建线程并执行传进来的任务
@Override
public void execute(Runnable command) {
threadFactory.newThread(command).start();
}
}
②创建NioEventLoop
public class NioEventLoopGroup extends MultithreadEventLoopGroup {
@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
EventLoopTaskQueueFactory queueFactory = args.length == 4 ? (EventLoopTaskQueueFactory) args[3] : null;
return new NioEventLoop(this, executor, (SelectorProvider) args[0],
((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2], queueFactory);
}
}
查看上面创建NioEventLoop
实例的构造方法,里面主要有下面几个重要参数。
public final class NioEventLoop extends SingleThreadEventLoop {
// Java NIO 中的 java.nio.channels.Selector
// 用来绑定Socket事件
private Selector selector;
private Selector unwrappedSelector;
/**
* 父类 SingleThreadEventExecutor 中
* 有个任务队列, 用来存放提交的任务
* private final Queue taskQueue;
*
* 具体实现是 ThreadPerTaskExecutor, 用来创建线程并执行任务
* private final Executor executor;
*
* 通过 ThreadPerTaskExecutor 创建出来的 FastThreadLocalThread
* private volatile Thread thread;
*/
}
NioEventLoop
是如何执行任务的?在这里打个断点,跟踪源码进去查看
bossGroup.next().execute(() -> {
System.out.println(Thread.currentThread() + ":" + LocalDateTime.now().toString());
});
NioEventLoop
执行任务的入口在父类SingleThreadEventExecutor
的execute
方法
public abstract class SingleThreadEventExecutor extends AbstractScheduledEventExecutor implements OrderedEventExecutor {
// 任务队列
private final Queue<Runnable> taskQueue;
// 执行线程
private volatile Thread thread;
// 具体实现是 ThreadPerTaskExecutor
private final Executor executor;
// 1. NioEventLoop执行任务的入口
@Override
public void execute(Runnable task) {
ObjectUtil.checkNotNull(task, "task");
execute(task, !(task instanceof LazyRunnable) && wakesUpForTask(task));
}
private void execute(Runnable task, boolean immediate) {
// 判断调用线程是否等于内部线程 this.thread == Thread.currentThread()
boolean inEventLoop = inEventLoop();
// 放入任务队列
addTask(task);
if (!inEventLoop) {
// inEventLoop为false, 外部线程调用NioEventLoop
// 启动线程
startThread();
// 省略......
}
// 省略......
}
private void startThread() {
// 这里会判断, 只能启动一次
if (state == ST_NOT_STARTED) {
// CAS操作
if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
boolean success = false;
try {
// 开始创建线程
doStartThread();
success = true;
} finally {
if (!success) {
STATE_UPDATER.compareAndSet(this, ST_STARTED, ST_NOT_STARTED);
}
}
}
}
}
private void doStartThread() {
assert thread == null;
// 这里执行的是ThreadPerTaskExecutor.execute()方法, 会创建线程并执行传进去的Runnable任务
executor.execute(new Runnable() {
@Override
public void run() {
// Thread.currentThread()返回的是上面调用ThreadPerTaskExecutor.execute()方法生成的线程
thread = Thread.currentThread();
// 省略......
try {
// 这里会执行NioEventLoop里面的run方法
SingleThreadEventExecutor.this.run();
success = true;
} catch (Throwable t) {
logger.warn("Unexpected exception from an event executor: ", t);
} finally {
// 省略......
}
}
});
}
}
NioEventLoop
的run
方法里面是一个死循环,里面负责监听Selector
上的IO事件和执行提交进来的异步任务。
public final class NioEventLoop extends SingleThreadEventLoop {
private Selector selector;
private Selector unwrappedSelector;
private final IntSupplier selectNowSupplier = new IntSupplier() {
@Override
public int get() throws Exception {
return selectNow();
}
};
int selectNow() throws IOException {
return selector.selectNow();
}
@Override
protected void run() {
// 记录发生空转的次数, 既没有IO需要处理、也没有执行任何任务
int selectCnt = 0;
for (;;) {
try {
int strategy;
try {
// hasTasks() 是判断任务队列是否有任务
// 如果 hasTasks()为true, 则返回selectNowSupplier.get(), 否则返回 SelectStrategy.SELECT
strategy = selectStrategy.calculateStrategy(selectNowSupplier, hasTasks());
switch (strategy) {
case SelectStrategy.CONTINUE: // -2
continue;
case SelectStrategy.BUSY_WAIT: // -3
// fall-through to SELECT since the busy-wait is not supported with NIO
case SelectStrategy.SELECT: // -1
// 获取下一个定时任务执行时间
long curDeadlineNanos = nextScheduledTaskDeadlineNanos();
if (curDeadlineNanos == -1L) {
curDeadlineNanos = NONE; // nothing on the calendar
}
nextWakeupNanos.set(curDeadlineNanos);
try {
// 再次判断有没有任务
if (!hasTasks()) {
// key 调用select
strategy = select(curDeadlineNanos);
}
} finally {
// This update is just to help block unnecessary selector wakeups
// so use of lazySet is ok (no race condition)
nextWakeupNanos.lazySet(AWAKE);
}
// fall through
default:
}
} catch (IOException e) {
// If we receive an IOException here its because the Selector is messed up. Let's rebuild
// the selector and retry. https://github.com/netty/netty/issues/8566
// 出现异常, 重新构建Selector并注册事件
rebuildSelector0();
selectCnt = 0;
handleLoopException(e);
continue;
}
// 计算器加1
selectCnt++;
cancelledKeys = 0;
needsToSelectAgain = false;
// 控制处理执行io事件时间的占用比例, 默认是百分之50
// 一半时间用来处理io事件, 一半时间用来处理任务队列taskQueue里面的任务
final int ioRatio = this.ioRatio;
boolean ranTasks;
if (ioRatio == 100) {
try {
if (strategy > 0) {
// 处理IO事件
processSelectedKeys();
}
} finally {
// Ensure we always run tasks.
// key 执行taskQueue中全部的任务
ranTasks = runAllTasks();
}
} else if (strategy > 0) { // 如果 ioRatio != 100, 优先处理IO事件
final long ioStartTime = System.nanoTime();
try {
// 处理IO事件
processSelectedKeys();
} finally {
// 计算处理IO花费的时间
// Ensure we always run tasks.
final long ioTime = System.nanoTime() - ioStartTime;
// key ioTime * (100 - ioRatio) / ioRatio 是计算任务队列执行的时间
ranTasks = runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
} else {
// 0 代表运行最小数量的任务, 即运行63个任务
ranTasks = runAllTasks(0); // This will run the minimum number of tasks
}
// 执行了任务队列的任务或者是有IO事件
if (ranTasks || strategy > 0) {
if (selectCnt > MIN_PREMATURE_SELECTOR_RETURNS && logger.isDebugEnabled()) {
logger.debug("Selector.select() returned prematurely {} times in a row for Selector {}.",
selectCnt - 1, selector);
}
selectCnt = 0;
} else if (unexpectedSelectorWakeup(selectCnt)) { // Unexpected wakeup (unusual case)
selectCnt = 0;
// 如果(!ranTasks && strategy <= 0) 即任务队里面没有任务, 也没有IO事件
// 则会执行 unexpectedSelectorWakeup(selectCnt)
// 如果 selectCnt 达到一定最大值(默认为512), 重新构建Selector并注册事件, 防止 JDK BUG空轮训
// 这个BUG好像是虽然调用了阻塞的selector.select()
// 但是由于操作系统底层发现socket断开,还是会返回0,然后又没能处理相应的事件
// 而且任务队列也为空的情况下,就会死循环下去,造成CPU100%
// Netty的解决方案就是用了一个变量selectCnt统计轮询的次数。
}
} catch (CancelledKeyException e) {
// Harmless exception - log anyway
if (logger.isDebugEnabled()) {
logger.debug(CancelledKeyException.class.getSimpleName() + " raised by a Selector {} - JDK bug?",
selector, e);
}
} catch (Error e) {
throw (Error) e;
} catch (Throwable t) {
handleLoopException(t);
} finally {
// Always handle shutdown even if the loop processing threw an exception.
try {
if (isShuttingDown()) {
closeAll();
if (confirmShutdown()) {
return;
}
}
} catch (Error e) {
throw (Error) e;
} catch (Throwable t) {
handleLoopException(t);
}
}
}
}
}
根据deadlineNanos的值选择是无限期阻塞还是阻塞一段时间直接返回
public final class NioEventLoop extends SingleThreadEventLoop {
private int select(long deadlineNanos) throws IOException {
if (deadlineNanos == NONE) {
// 无限期阻塞
return selector.select();
}
// Timeout will only be 0 if deadline is within 5 microsecs
// 如果deadlineNanos小于5纳秒, 则为0, 否则取整为1毫秒
// 这段操作是为了向上取整, 转成毫秒
long timeoutMillis = deadlineToDelayNanos(deadlineNanos + 995000L) / 1000000L;
// 不阻塞的selectNow()和阻塞一段时间的select(timeout)
return timeoutMillis <= 0 ? selector.selectNow() : selector.select(timeoutMillis);
}
}
timeoutNanos为0时,最多只能执行63个任务。
timeoutNanos大于0时,如果已执行任务的数量加1是64的整数倍,执行时间超过timeoutNanos则先停下来返回。
public abstract class SingleThreadEventExecutor extends AbstractScheduledEventExecutor implements OrderedEventExecutor {
protected boolean runAllTasks(long timeoutNanos) {
// 这里会将定时任务队列里面的可执行任务拿出来放到任务队列里
fetchFromScheduledTaskQueue();
// 从任务队列里面获取任务
Runnable task = pollTask();
if (task == null) {
// 去执行tailTasks的任务, 暂时还不知道这个是什么情况添加的任务
afterRunningAllTasks();
return false;
}
// 截至时间, 已经花费调度任务的时间+超时时间
// ScheduledFutureTask.nanoTime() 表示从开始到现在执行任务持续的时间
final long deadline = timeoutNanos > 0 ? ScheduledFutureTask.nanoTime() + timeoutNanos : 0;
// 记录执行任务数量
long runTasks = 0;
long lastExecutionTime;
for (;;) {
// 执行任务
safeExecute(task);
// 执行任务数量加1
runTasks ++;
// Check timeout every 64 tasks because nanoTime() is relatively expensive.
// XXX: Hard-coded value - will make it configurable if it is really a problem.
if ((runTasks & 0x3F) == 0) {
// 当执行任务到达一定量时计算下时间
lastExecutionTime = ScheduledFutureTask.nanoTime();
// 如果调度任务的时间超过截止时间了, 那就退出了, 否则时间太长了
if (lastExecutionTime >= deadline) {
break;
}
}
// 继续获取任务
task = pollTask();
if (task == null) {
lastExecutionTime = ScheduledFutureTask.nanoTime();
break;
}
}
// 这里会执行tailTasks里面的任务
afterRunningAllTasks();
this.lastExecutionTime = lastExecutionTime;
return true;
}
}
将任务队列的任务全部执行完后再返回。
public abstract class SingleThreadEventExecutor extends AbstractScheduledEventExecutor implements OrderedEventExecutor {
protected boolean runAllTasks() {
assert inEventLoop();
boolean fetchedAll;
boolean ranAtLeastOne = false;
do {
// 这里会将定时任务队列里面的可执行任务拿出来放到任务队列里
fetchedAll = fetchFromScheduledTaskQueue();
// 执行队列里面的所有任务
if (runAllTasksFrom(taskQueue)) {
ranAtLeastOne = true;
}
} while (!fetchedAll); // keep on processing until we fetched all scheduled tasks.
// while循环保证定时任务都能被执行完
if (ranAtLeastOne) {
lastExecutionTime = ScheduledFutureTask.nanoTime();
}
// 这里会执行tailTasks里面的任务
afterRunningAllTasks();
return ranAtLeastOne;
}
}
NioEventLoop
都有一个自己的java.nio.channels.Selector
,可以往这个Selector
上注册我们感兴趣的事件。NioEventLoop
都有一个自己的任务队列,提交的任务先放入到这个队列。NioEventLoop
都有一个线程Thread
。ChannelHandler
是核心业务处理接口,用于处理或拦截IO
事件,并将其转发到ChannelPipeline
中的下一个ChannelHandler
,运用的是责任链设计模式。
ChannelHandler
分为入站和出站两种:ChannelInboundHandler
和ChannelOutboundHandler
,一般不建议直接实现这两个接口,而是它们的抽象类:
SimpleChannelInboundHandler
:处理入站事件,它可以帮我们做资源的自动释放等操作。不建议直接使用 ChannelInboundHandlerAdapter
。ChannelOutboundHandlerAdapter
:处理出站事件。ChannelDuplexHandler
:既可以处理入站也可以处理出站。默认实现DefaultChannelHandlerContext
,里面有ChannelHandler
和ChannelPipeline
的引用。
往ChannelPipeline
中的节点就是一个个ChannelHandlerContext
。
ChannelPipeline
是 ChannelHandler
的集合,它负责处理和拦截入站和出站的事件和操作,每个Channel
都有一个ChannelPipeline
与之对应,会自动创建。
ChannelPipeline
中存储的是ChannelHandlerContext
链,通过这个链把ChannelHandler
连接起来。
Channel
对应一个ChannelPipeline
ChannelPipeline
包含一条双向的ChannelHandlerContext
链ChannelHandlerContext
中包含一个ChannelHandler
Channel
会绑定到一个EventLoop
上NioEventLoop
维护了一个Selector
(使用的是 Java 原生的 Selector
)NioEventLoop
相当于一个线程Netty版本
<dependency>
<groupId>io.nettygroupId>
<artifactId>netty-allartifactId>
<version>4.1.63.Finalversion>
dependency>