阅读须知
- Netty版本:4.1.14.Final
- 文章中使用/* */注释的方法会做深入分析
正文
Netty的EventLoop主要用于处理I/O操作,而NioEventLoop就是EventLoop的主要实现之一,它将Channel注册到Selector,因此在事件循环中可以对这些进行多路复用。我们首先来看NioEventLoop的层次结构:
我们来看它的构造方法:
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
// 调用父类构造方法初始化任务队列和各个属性
super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
if (selectorProvider == null) {
throw new NullPointerException("selectorProvider");
}
if (strategy == null) {
throw new NullPointerException("selectStrategy");
}
provider = selectorProvider;
/* 开启多路复用器 */
final SelectorTuple selectorTuple = openSelector();
selector = selectorTuple.selector;
unwrappedSelector = selectorTuple.unwrappedSelector;
selectStrategy = strategy;
}
NioEventLoop:
private SelectorTuple openSelector() {
final Selector unwrappedSelector;
try {
unwrappedSelector = provider.openSelector();
} catch (IOException e) {
throw new ChannelException("failed to open a new selector", e);
}
if (DISABLE_KEYSET_OPTIMIZATION) {
return new SelectorTuple(unwrappedSelector);
}
final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();
Object maybeSelectorImplClass = AccessController.doPrivileged(new PrivilegedAction<Object> () {
@Override
public Object run() {
try {
return Class.forName(
"sun.nio.ch.SelectorImpl",
false,
PlatformDependent.getSystemClassLoader());
} catch (Throwable cause) {
return cause;
}
}
});
if (!(maybeSelectorImplClass instanceof Class) ||
!((Class<?>) maybeSelectorImplClass).isAssignableFrom(unwrappedSelector.getClass())) {
if (maybeSelectorImplClass instanceof Throwable) {
Throwable t = (Throwable) maybeSelectorImplClass;
logger.trace("failed to instrument a special java.util.Set into: {}", unwrappedSelector, t);
}
return new SelectorTuple(unwrappedSelector);
}
final Class<?> selectorImplClass = (Class<?>) maybeSelectorImplClass;
Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object> () {
@Override
public Object run() {
try {
Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");
Throwable cause = ReflectionUtil.trySetAccessible(selectedKeysField);
if (cause != null) {
return cause;
}
cause = ReflectionUtil.trySetAccessible(publicSelectedKeysField);
if (cause != null) {
return cause;
}
selectedKeysField.set(unwrappedSelector, selectedKeySet);
publicSelectedKeysField.set(unwrappedSelector, selectedKeySet);
return null;
} catch (NoSuchFieldException e) {
return e;
} catch (IllegalAccessException e) {
return e;
}
}
});
if (maybeException instanceof Exception) {
selectedKeys = null;
Exception e = (Exception) maybeException;
logger.trace("failed to instrument a special java.util.Set into: {}", unwrappedSelector, e);
return new SelectorTuple(unwrappedSelector);
}
selectedKeys = selectedKeySet;
logger.trace("instrumented a special java.util.Set into: {}", unwrappedSelector);
return new SelectorTuple(unwrappedSelector,
new SelectedSelectionKeySetSelector(unwrappedSelector, selectedKeySet));
}
方法很长,首先调用JDK NIO的API开启Selector,剩余核心逻辑就是根据设置来判断是否采取selectedKeys优化功能。
在上面的结构图中我们看到,NioEventLoop还实现了JDK的Executor,熟悉线程池的同学对这个接口一定不陌生,所以NioEventLoop也同样可以执行Runnable任务,在Netty中NioEventLoop主要负责执行一些系统任务和定时任务,我们来看相关方法实现:
SingleThreadEventExecutor:
public void execute(Runnable task) {
if (task == null) {
throw new NullPointerException("task");
}
// 判断当前线程是否是当前EventLoop中的线程
boolean inEventLoop = inEventLoop();
if (inEventLoop) {
// 添加任务到队列中等待执行,添加失败走拒绝策略
addTask(task);
} else {
startThread(); /* 启动线程 */
// 添加任务到队列中等待执行,添加失败走拒绝策略
addTask(task);
if (isShutdown() && removeTask(task)) {
// 如果线程已关闭并且成功从队列中移除了任务,则抛出异常拒绝任务
reject();
}
}
// addTaskWakesUp在NioEventLoop构造方法中传入的是false
// 判断task是否不是NonWakeupRunnable类型
if (!addTaskWakesUp && wakesUpForTask(task)) {
/* 唤醒selector */
wakeup(inEventLoop);
}
}
先来看wakeup方法的实现:
NioEventLoop:
protected void wakeup(boolean inEventLoop) {
if (!inEventLoop && wakenUp.compareAndSet(false, true)) {
// 可以让尚未返回的第一个select操作立即返回
selector.wakeup();
}
}
NioEventLoop:
private void startThread() {
if (state == ST_NOT_STARTED) {
// CAS变更线程状态,保证只有一次成功
if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
/* 启动线程 */
doStartThread();
}
}
}
SingleThreadEventExecutor:
private void doStartThread() {
assert thread == null;
executor.execute(new Runnable() {
@Override
public void run() {
thread = Thread.currentThread();
if (interrupted) {
thread.interrupt();
}
boolean success = false;
updateLastExecutionTime();
try {
/* 子类实现run方法实现任务逻辑 */
SingleThreadEventExecutor.this.run();
success = true;
} catch (Throwable t) {
logger.warn("Unexpected exception from an event executor: ", t);
} finally {
// 自旋更新线程状态为关闭
for (;;) {
int oldState = state;
if (oldState >= ST_SHUTTING_DOWN || STATE_UPDATER.compareAndSet(
SingleThreadEventExecutor.this, oldState, ST_SHUTTING_DOWN)) {
break;
}
}
if (success && gracefulShutdownStartTime == 0) {
logger.error("Buggy " + EventExecutor.class.getSimpleName() + " implementation; " +
SingleThreadEventExecutor.class.getSimpleName() + ".confirmShutdown() must be called " +
"before run() implementation terminates.");
}
try {
for (;;) {
// 运行所有剩余的任务和关闭钩子
if (confirmShutdown()) {
break;
}
}
} finally {
try {
// 子类实现清理操作,默认空实现
// NioEventLoop实现的清理操作就是关闭selector
cleanup();
} finally {
// 更新线程状态为终止
STATE_UPDATER.set(SingleThreadEventExecutor.this, ST_TERMINATED);
threadLock.release(); // 释放线程锁
if (!taskQueue.isEmpty()) {
logger.warn(
"An event executor terminated with " +
"non-empty task queue (" + taskQueue.size() + ')');
}
terminationFuture.setSuccess(null);
}
}
}
}
});
}
NioEventLoop:
protected void run() {
for (;;) {
try {
switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.SELECT:
/* select操作 */
select(wakenUp.getAndSet(false));
if (wakenUp.get()) {
selector.wakeup();
}
default:
}
cancelledKeys = 0;
needsToSelectAgain = false;
// ioRatio的含义是event loop中I/O所需时间的百分比
// 默认值为50,这意味着event loop将尝试花费与非I/O任务相同的I/O时间
final int ioRatio = this.ioRatio;
if (ioRatio == 100) {
try {
/* 处理selectKey */
processSelectedKeys();
} finally {
/* 确保我们一直运行任务 */
runAllTasks();
}
} else {
final long ioStartTime = System.nanoTime();
try {
/* 处理selectKey */
processSelectedKeys();
} finally {
final long ioTime = System.nanoTime() - ioStartTime;
/* 确保我们一直运行任务,有超时时间 */
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
}
} catch (Throwable t) {
handleLoopException(t);
}
// 即使循环处理引发异常,也始终处理shutdown
try {
if (isShuttingDown()) {
/* 关闭所有Channel */
closeAll();
/* 确认shutdown */
if (confirmShutdown()) {
return;
}
}
} catch (Throwable t) {
handleLoopException(t);
}
}
}
下面我们就来分析比较关键的select操作:
NioEventLoop:
private void select(boolean oldWakenUp) throws IOException {
Selector selector = this.selector;
try {
int selectCnt = 0;
long currentTimeNanos = System.nanoTime();
// 计算下一个将要触发的定时任务的超时时间
// delayNanos方法会获取到下一个定时任务的触发时间
long selectDeadLineNanos = currentTimeNanos + delayNanos(currentTimeNanos);
for (;;) {
// 为超时时间增加五毫秒的调整值
long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000 L) / 1000000 L;
if (timeoutMillis <= 0) {
if (selectCnt == 0) {
// 如果需要立即执行或者已经超时,则执行轮询操作
selector.selectNow();
// select计数器置为1
selectCnt = 1;
}
break;
}
// 如果在wakenUp值为true时提交了任务,则该任务没有机会调用Selector的wakeup方法
// 所以我们需要在执行select操作之前再次检查任务队列
// 如果我们不这样做,那么任务可能会被挂起,直到select操作超时
// 如果IdleStateHandler存在于pipeline中,它可能会被挂起直到idle timeout
if (hasTasks() && wakenUp.compareAndSet(false, true)) {
selector.selectNow();
selectCnt = 1;
break;
}
// 剩余的超时时间作为参数调用Seletor的select方法
int selectedKeys = selector.select(timeoutMillis);
// 自增select计数器
selectCnt++;
if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) {
// 以下情况退出当前循环
// 1、select操作selectedKeys不为0,说明有读写事件需要处理
// 2、由用户唤醒多路复用器或任务队列具有待处理任务
// 3、定时任务已准备好进行处理
break;
}
if (Thread.interrupted()) {
// 线程被中断,因此重置selected keys并退出循环,这样就不会在繁忙的循环中执行
// 由于这很可能是用户或其客户端库的处理程序中的错误,我们也会记录它
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) {
// timeoutMillis已过,select没有任何内容
selectCnt = 1;
} else if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 &&
selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) {
// selector连续多次select没有内容,有可能触发了JDK NIO epoll死循环导致CPU 100%的bug
// 重建selector解决
logger.warn(
"Selector.select() returned prematurely {} times in a row; rebuilding Selector {}.",
selectCnt, selector);
/* 重建selector */
rebuildSelector();
selector = this.selector;
// 再次select以填充selectedKeys
selector.selectNow();
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);
}
}
}
NioEventLoop:
public void rebuildSelector() {
if (!inEventLoop()) {
execute(new Runnable() {
@Override
public void run() {
/* 当前线程不在event loop中,向executor提交新任务来重建selector */
rebuildSelector0();
}
});
return;
}
/* 重建selector */
rebuildSelector0();
}
NioEventLoop:
private void rebuildSelector0() {
final Selector oldSelector = selector;
final SelectorTuple newSelectorTuple;
if (oldSelector == null) {
return;
}
try {
// 创建新的selector
newSelectorTuple = openSelector();
} catch (Exception e) {
logger.warn("Failed to create a new Selector.", e);
return;
}
// 注册所有的channel到新的selector
int nChannels = 0;
for (SelectionKey key: oldSelector.keys()) {
Object a = key.attachment();
try {
if (!key.isValid() || key.channel().keyFor(newSelectorTuple.unwrappedSelector) != null) {
continue;
}
int interestOps = key.interestOps();
// 取消select key并取消channel在selector上的注册
key.cancel();
// 将channel注册到新的selector
SelectionKey newKey = key.channel().register(newSelectorTuple.unwrappedSelector, interestOps, a);
if (a instanceof AbstractNioChannel) {
((AbstractNioChannel) a).selectionKey = newKey;
}
nChannels++;
} catch (Exception e) {
logger.warn("Failed to re-register a Channel to the new Selector.", e);
if (a instanceof AbstractNioChannel) {
AbstractNioChannel ch = (AbstractNioChannel) a;
ch.unsafe().close(ch.unsafe().voidPromise());
} else {
@SuppressWarnings("unchecked")
NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
invokeChannelUnregistered(task, key, e);
}
}
}
selector = newSelectorTuple.selector;
unwrappedSelector = newSelectorTuple.unwrappedSelector;
try {
// 关闭旧的selector,因为所有channel已经注册到新的selector
oldSelector.close();
} catch (Throwable t) {
if (logger.isWarnEnabled()) {
logger.warn("Failed to close the old Selector.", t);
}
}
logger.info("Migrated " + nChannels + " channel(s) to the new Selector.");
}
这样,select方法就分析完成了,接下来肯定就是select key的处理了:
NioEventLoop:
private void processSelectedKeys() {
if (selectedKeys != null) {
/* 开启优化的select key处理 */
processSelectedKeysOptimized();
} else {
// 不开启优化的select key处理
processSelectedKeysPlain(selector.selectedKeys());
}
}
两个分支处理select key的逻辑是比较相似的,我们以开启优化的方式为例:
NioEventLoop:
private void processSelectedKeysOptimized() {
for (int i = 0; i < selectedKeys.size; ++i) {
final SelectionKey k = selectedKeys.keys[i];
// 将数组中已经取出的元素置为null,以便channel关闭时GC掉
selectedKeys.keys[i] = null;
final Object a = k.attachment();
if (a instanceof AbstractNioChannel) {
/* 处理select key */
processSelectedKey(k, (AbstractNioChannel) a);
} else {
@SuppressWarnings("unchecked")
NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
processSelectedKey(k, task);
}
// needsToSelectAgain变量在run方法进入之前置为false
if (needsToSelectAgain) {
// 这里也是为了GC
selectedKeys.reset(i + 1);
// 如果需要再次select则再次调用selecor的selectNow方法
selectAgain();
i = -1;
}
}
}
NioEventLoop:
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
if (!k.isValid()) {
final EventLoop eventLoop;
try {
eventLoop = ch.eventLoop();
} catch (Throwable ignored) {
// 如果Channel实现因为没有event loop而抛出异常,我们会忽略它
// 因为我们只是试图确定channel是否已注册到此event loop,因此有权关闭channel
return;
}
// 如果channel仍然注册到此EventLoop,则仅关闭channel
// channel可以从event loop中取消注册,因此SelectionKey可以作为注销过程的一部分被取消
// 但是该channel仍然是健康的,不应该关闭
if (eventLoop != this || eventLoop == null) {
return;
}
// 如果SelectionKey无效则关闭channel
unsafe.close(unsafe.voidPromise());
return;
}
try {
int readyOps = k.readyOps();
// 我们首先需要在尝试触发read或write之前调用finishConnect
// 否则NIO JDK channel实现可能会抛出NotYetConnectedException
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
// 移除OP_CONNECT,否则Selector.select将始终返回而不会阻塞
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
unsafe.finishConnect();
}
// 首先处理OP_WRITE,因为我们可以编写一些排队的缓冲区,因此可以释放内存
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
// 调用forceFlush,一旦没有什么可写的,它也将清除OP_WRITE
ch.unsafe().forceFlush();
}
// 同时检查readOps为0以解决可能导致死循环的JDK bug
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
// 读操作
unsafe.read();
}
} catch (CancelledKeyException ignored) {
// 异常关闭channel
unsafe.close(unsafe.voidPromise());
}
}
这里的各种对Channel的操作,如close、read等,我们在分析ServerSocketChannel的相关源码中分析过,这里不再重复。处理完select key之后,需要在finally块中保证任务一直运行:
SingleThreadEventExecutor:
protected boolean runAllTasks(long timeoutNanos) {
/* 从定时任务队列中获取任务 */
fetchFromScheduledTaskQueue();
// 从队列中轮询task,并且会检查是否需要重新select
Runnable task = pollTask();
if (task == null) {
// 子类扩展实现任务执行结束之后的操作
// SingleThreadEventLoop覆盖此方法完成自己维护的tailTasks队列中任务的执行
afterRunningAllTasks();
return false;
}
// 计算过期时间
final long deadline = ScheduledFutureTask.nanoTime() + timeoutNanos;
long runTasks = 0;
long lastExecutionTime;
for (;;) {
// 执行task的run方法,会捕捉异常避免抛出对上层的影响
safeExecute(task);
runTasks++;
// 每64个任务检查超时,因为nanoTime相对昂贵
if ((runTasks & 0x3F) == 0) {
lastExecutionTime = ScheduledFutureTask.nanoTime();
// 超时退出循环
if (lastExecutionTime >= deadline) {
break;
}
}
// 继续从队列中获取任务
task = pollTask();
if (task == null) {
// 获取不到任务记录最后的执行时间并退出循环
lastExecutionTime = ScheduledFutureTask.nanoTime();
break;
}
}
// 同样子类扩展实现任务执行结束之后的操作
afterRunningAllTasks();
this.lastExecutionTime = lastExecutionTime;
return true;
}
SingleThreadEventExecutor:
private boolean fetchFromScheduledTaskQueue() {
long nanoTime = AbstractScheduledEventExecutor.nanoTime();
// 从scheduledTaskQueue中获取任务
Runnable scheduledTask = pollScheduledTask(nanoTime);
while (scheduledTask != null) {
// 取出任务放入taskQueue中
if (!taskQueue.offer(scheduledTask)) {
// 任务队列中没有剩余空间将其添加回scheduledTaskQueue,因此我们可以再次提取它
scheduledTaskQueue().add((ScheduledFutureTask<?>) scheduledTask);
return false;
}
// 继续循环获取任务
scheduledTask = pollScheduledTask(nanoTime);
}
return true;
}
回到run方法,接下来就是shutdown流程了:
NioEventLoop:
private void closeAll() {
selectAgain();
Set<SelectionKey> keys = selector.keys();
Collection<AbstractNioChannel> channels = new ArrayList<AbstractNioChannel>(keys.size());
for (SelectionKey k: keys) {
// 循环从SelectionKey中获取channel放入channels集合中
Object a = k.attachment();
if (a instanceof AbstractNioChannel) {
channels.add((AbstractNioChannel) a);
} else {
k.cancel();
@SuppressWarnings("unchecked")
NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
invokeChannelUnregistered(task, k, null);
}
}
// 遍历channels集合关闭channel
for (AbstractNioChannel ch: channels) {
ch.unsafe().close(ch.unsafe().voidPromise());
}
}
NioEventLoop:
protected boolean confirmShutdown() {
if (!isShuttingDown()) {
return false;
}
if (!inEventLoop()) {
throw new IllegalStateException("must be invoked from an event loop");
}
/* 取消定时任务 */
cancelScheduledTasks();
// gracefulShutdownStartTime为优雅停机开始时间
if (gracefulShutdownStartTime == 0) {
gracefulShutdownStartTime = ScheduledFutureTask.nanoTime();
}
// 运行所有剩余的定时任务,并运行关闭钩子
if (runAllTasks() || runShutdownHooks()) {
if (isShutdown()) {
return true;
}
// gracefulShutdownQuietPeriod为安静期
// 队列中有任务,稍等一会,直到没有任务排队等待安静期,或者如果安静期为0则终止
if (gracefulShutdownQuietPeriod == 0) {
return true;
}
// 这里的wakeup方法上文已经分析过
wakeup(true);
return false;
}
final long nanoTime = ScheduledFutureTask.nanoTime();
if (isShutdown() || nanoTime - gracefulShutdownStartTime > gracefulShutdownTimeout) {
return true;
}
if (nanoTime - lastExecutionTime <= gracefulShutdownQuietPeriod) {
// 每100ms检查是否有任何任务添加到队列中
wakeup(true);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
return false;
}
// 在最后的安静期没有添加任务,希望可以安全地关闭
return true;
}
到这里,NioEventLoop的源码解析就完成了。