private void select(boolean oldWakenUp) throws IOException {
Selector selector = this.selector;
try {
// select操作计数
int selectCnt = 0;
// 记录当前系统时间
long currentTimeNanos = System.nanoTime();
// delayNanos方法用于计算定时任务队列,最近一个任务的截止时间
// selectDeadLineNanos 表示当前select操作所不能超过的最大截止时间
long selectDeadLineNanos = currentTimeNanos + delayNanos(currentTimeNanos);
for (;;) {
// 计算超时时间,判断是否超时
long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L;
// 如果 timeoutMillis <= 0, 表示超时,进行一个非阻塞的 select 操作。设置 selectCnt 为 1. 并终止本次循环。
if (timeoutMillis <= 0) {
if (selectCnt == 0) {
selector.selectNow();
selectCnt = 1;
}
break;
}
// 当wakenUp为ture时,恰好有task被提交,这个task将无法获得调用的机会
// Selector#wakeup. 因此,在执行select操作之前,需要再次检查任务队列
// 如果不这么做,这个Task将一直挂起,直到select操作超时
// 如果 pipeline 中存在 IdleStateHandler ,那么Task将一直挂起直到 空闲超时。
if (hasTasks() && wakenUp.compareAndSet(false, true)) {
// 调用非阻塞方法
selector.selectNow();
selectCnt = 1;
break;
}
// 如果当前任务队列为空,并且超时时间未到,则进行一个阻塞式的selector操作。timeoutMillis 为最大的select时间
int selectedKeys = selector.select(timeoutMillis);
// 操作计数 +1
selectCnt ++;
// 存在以下情况,本次selector则终止
if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) {
// - 轮训到了事件(Selected something,)
// - 被用户唤醒(waken up by user,)
// - 已有任务队列(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();
// 如果time > currentTimeNanos + timeoutMillis(超时时间),则表明已经执行过一次select操作
if (time - TimeUnit.MILLISECONDS.toNanos(timeoutMillis) >= currentTimeNanos) {
// timeoutMillis elapsed without anything selected.
selectCnt = 1;
}
// 如果 time <= currentTimeNanos + timeoutMillis,表示触发了空轮训
// 如果空轮训的次数超过 SELECTOR_AUTO_REBUILD_THRESHOLD (512),则重建一个新的selctor,避免空轮训
else if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 &&
selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) {
// The selector returned prematurely many times in a row.
// Rebuild the selector to work around the problem.
logger.warn(
"Selector.select() returned prematurely {} times in a row; rebuilding Selector {}.",
selectCnt, selector);
// 重建创建一个新的selector
rebuildSelector();
selector = this.selector;
// Select again to populate selectedKeys.
// 对重建后的selector进行一次非阻塞调用,用于获取最新的selectedKeys
selector.selectNow();
// 设置select计数
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
}
}
private void processSelectedKeysOptimized() {
for (int i = 0; i < selectedKeys.size; ++i) {
final SelectionKey k = selectedKeys.keys[i];
// null out entry in the array to allow to have it GC'ed once the Channel close
// See https://github.com/netty/netty/issues/2363
// 设置为null,有利于GC回收
selectedKeys.keys[i] = null;
// 获取 SelectionKey 中的 attachment, 我们这里就是 NioChannel
final Object a = k.attachment();
if (a instanceof AbstractNioChannel) {
// 处理 SelectedKey
processSelectedKey(k, (AbstractNioChannel) a);
} else {
@SuppressWarnings("unchecked")
NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
processSelectedKey(k, task);
}
if (needsToSelectAgain) {
// null out entries in the array to allow to have it GC'ed once the Channel close
// See https://github.com/netty/netty/issues/2363
selectedKeys.reset(i + 1);
selectAgain();
i = -1;
}
}
}
// 处理 SelectedKey
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
// 获取Netty Channel中的 NioUnsafe 对象,用于后面的IO操作
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
// 判断 SelectedKey 的有效性,如果无效,则直接返回并关闭channel
if (!k.isValid()) {
final EventLoop eventLoop;
try {
eventLoop = ch.eventLoop();
} catch (Throwable ignored) {
// If the channel implementation throws an exception because there is no event loop, we ignore this
// because we are only trying to determine if ch is registered to this event loop and thus has authority
// to close ch.
return;
}
// Only close ch if ch is still registered to this EventLoop. ch could have deregistered from the event loop
// and thus the SelectionKey could be cancelled as part of the deregistration process, but the channel is
// still healthy and should not be closed.
// See https://github.com/netty/netty/issues/5125
if (eventLoop != this || eventLoop == null) {
return;
}
// close the channel if the key is not valid anymore
// 关闭channel
unsafe.close(unsafe.voidPromise());
return;
}
try {
// 获取 SelectionKey 中所有准备就绪的操作集
int readyOps = k.readyOps();
// We first need to call finishConnect() before try to trigger a read(...) or write(...) as otherwise
// the NIO JDK channel implementation may throw a NotYetConnectedException.
// 在调用处理READ与WRITE事件之间,先调用finishConnect()接口,避免异常 NotYetConnectedException 发生。
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
// remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking
// See https://github.com/netty/netty/issues/924
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
unsafe.finishConnect();
}
// Process OP_WRITE first as we may be able to write some queued buffers and so free memory.
// 处理 WRITE 事件
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
// Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write
ch.unsafe().forceFlush();
}
// Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead
// to a spin loop
// 处理 ACCEPT 与 READ 事件
// 如果当前的EventLoop是WorkGroup,则表示有 READ 事件
// 如果当前的EventLoop是BossGroup,则表示有 ACCEPT 事件,有新连接进来了
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
// 读取数据
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
}
}