NioEventLoop 执行逻辑
方法: NioEventLoop.run()
for(;; )
select() 检查是否有 IO 事件
processSelectedKeys() 处理上面检测出来的 IO 事件
runAllTasks() 处理异步任务队列
protected void run() {
for (;;) {
try {
try {
// 1. 调用 select() 查询是否有就绪的 I/O 事件
switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.BUSY_WAIT:
// fall-through to SELECT since the busy-wait is not supported with NIO
case SelectStrategy.SELECT:
select(wakenUp.getAndSet(false)); // select 操作, 设置唤醒状态为 false(要进行 select 操作, 且不为唤醒状态)
if (wakenUp.get()) {
selector.wakeup();
}
// fall through
default:
} // 1. 结束
}
// 2. 处理上面检测出来的 IO 事件
cancelledKeys = 0;
needsToSelectAgain = false;
final int ioRatio = this.ioRatio; // ioRatio 用于控制 selectedKeys 和 tasks 的执行时间. 默认值 50, 处理 selectedKeys 和 tasks 时间相同
if (ioRatio == 100) {
try {
processSelectedKeys(); // 处理就绪的 I/O 事件
} finally {
// Ensure we always run tasks.
runAllTasks(); // 执行外部线程放入任务队列中的任务
}
} else {
final long ioStartTime = System.nanoTime(); // 记录初始时间
try {
processSelectedKeys(); // 处理就绪的 I/O 事件
} finally {
// Ensure we always run tasks.
final long ioTime = System.nanoTime() - ioStartTime;
runAllTasks(ioTime * (100 - ioRatio) / ioRatio); // 给定时间内执行任务
}
}
} catch (Throwable t) {
handleLoopException(t);
}
}
select() 逻辑
方法: io.netty.channel.nio.NioEventLoop#select
计算截止时间, 时间不够, selectNow() 并返回
此时任务队列中有任务, selectNow() 并返回
阻塞式 select(截止时间没到, 并且任务队列为空, 则执行该步骤)
避免 jdk 空轮询 bug(销毁原有的 selector, 重建 selector, 将 attachment 和 op 注册到新的 selector 上)
private void select(boolean oldWakenUp) throws IOException {
Selector selector = this.selector;
try {
int selectCnt = 0;
long currentTimeNanos = System.nanoTime(); // 取当前时间
long selectDeadLineNanos = currentTimeNanos + delayNanos(currentTimeNanos); // selectDeadLineNanos 指可以进行 select 操作的截止时间点
for (;;) {
long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L; // 四舍五入将 select 操作时间换算为毫秒单位
if (timeoutMillis <= 0) { // 时间不足 1ms, 不再进行 select 操作
if (selectCnt == 0) { // 如果一次 select 操作没有进行
selector.selectNow(); // selecNow() 之后返回
selectCnt = 1;
}
break; // 循环出口
}
// ...
if (hasTasks() && wakenUp.compareAndSet(false, true)) { // 此时有任务进入队列 (任务队列中有任务需要执行) 且 唤醒标志为假 (并 cas 设置为 true)
selector.selectNow(); // selectNow() 返回,否则会耽误任务执行
selectCnt = 1;
break; // 循环出口
}
int selectedKeys = selector.select(timeoutMillis); // 阻塞式 select
selectCnt ++; // select 操作计数 ++
// 有就绪的 IO 事件, 参数 oldWakenUp 为真, 外部设置 wakenUp 为真, 有待执行普通任务, 有待执行调度任务
if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) {
// - Selected something,
// - waken up by user, or
// - the task queue has a pending task.
// - a scheduled task is ready for processing
break; // 循环出口
}
if (Thread.interrupted()) {
// Thread was interrupted so reset selected keys and break so we not run into a busy loop.
// As this is most likely a bug in the handler of the user or it's client library we will
// also log it.
//
// See https://github.com/netty/netty/issues/2426
if (logger.isDebugEnabled()) {
logger.debug("Selector.select() returned prematurely because" +
"Thread.currentThread().interrupt() was called. Use" +
"NioEventLoop.shutdownGracefully() to shutdown the NioEventLoop.");
}
selectCnt = 1;
break;
}
long time = System.nanoTime(); // 记录当前时间
if (time - TimeUnit.MILLISECONDS.toNanos(timeoutMillis) >= currentTimeNanos) { // 截止时间已到, 说明上面的阻塞式 select(timeout) 没有问题
// timeoutMillis elapsed without anything selected.
selectCnt = 1;
} else if (SELECTOR_AUTO_REBUILD_THRESHOLD> 0 &&
selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) { // 走到这说明发生空轮询 bug 了, 空轮询次数大于阈值
// The code exists in an extra method to ensure the method is not too big to inline as this
// branch is not very likely to get hit very frequently.
selector = selectRebuildSelector(selectCnt); // 重建 select, 避免 jdk 空轮询 bug
selectCnt = 1;
break;
}
currentTimeNanos = time;
}
if (selectCnt> MIN_PREMATURE_SELECTOR_RETURNS) {
if (logger.isDebugEnabled()) {
logger.debug("Selector.select() returned prematurely {} times in a row for Selector {}.",
selectCnt - 1, selector);
}
}
} catch (CancelledKeyException e) {
if (logger.isDebugEnabled()) {
logger.debug(CancelledKeyException.class.getSimpleName() + "raised by a Selector {} - JDK bug?",
selector, e);
}
// Harmless exception - log anyway
}
}