NioEventLoop基本介绍
在构建Netty服务时,首先需要创建一个NioEventLoopGroup,即如下代码:
···
NioEventLoopGroup boss = new NioEventLoopGroup(1);
NioEventLoopGroup worker = new NioEventLoopGroup();
···
- 初始化时机:NioEventLoopGroup内部定义了一个EventExecutor[] children成员变量(NioEventLoop是EventExecutor的子类),在初始化NioEventLoopGroup时也会初始化children,NioEventLoopGroup和NioEventLoop之间的关系有点类似于线程池和线程之间的关系,而NioEventLoop本身也具备线程池的特性,因此她们之间的关系类似线程池里面嵌套线程池的关系。
- 主要功能:NioEventLoop关联的线程会循环的调用selector.select()方法监听相关Channel上的事件,此外它还会基于一定的策略执行外部提交的任务。相关的Channel来自boss基于轮询的分发,保证了各个NioEventLoop之间的负载均衡。而外部任务主要包括向Channel中写数据以及用户提交的自定义任务
- 上述代码中boss定义一个NioEventLoop,这因为boss通常用于接收客户端连接请求。当处理链接请求出现瓶颈时可以基于多端口监听为boss创建更多的NioEventLoop;worker默认情况下将创建:处理器核数 * 2个NioEventLoop,boss获取到客户端连接请求后会将建立的Channel注册到worker中的一个NioEventLoop上,由该NioEventLoop来处理该Channel上的IO。
- NioEventLoop的类图结构如下:
- 实现了jdk的ScheduledExecutorService具备线程池的特性,下面简单的说明每个接口的功能作用
- EventExecutorGroup:实现了该接口,说明当我们调用EventExecutorGroup上的方法时实际上基于轮询方式调用内部EventExecutor的对应的方法。该接口提供了优雅关闭EventExecutor的相关方法,
- next():轮询内部EventExecutor
- shutdownGracefully():优雅关闭当前EventExecutorGroup,即优雅关闭内部EventExecutor
- terminationFuture():获取异步结束结果,当EventExecutorGroup关闭完成后该结果返回,在此之前在获取结果将被阻塞
- isShuttingDown():返回是否关闭完成
- 其他方法继承自jdk的接口的接口,仅更改了返回值返回Netty定义的高级异步结果(继承自jdk的Future)
- EventExecutor:提供用于判断一个线程是否是EventExecutor关联的内部线程。为避免多线程问题,对EventExecutor及其子类(NioEventLoop)的相关的配置方法的调用的通常基于任务形势提交到NioEventLoop让其关联线程执行,例如当外部线程将一个Channel注册到NioEventLoop时实际上并没有直接调用NioEventLoop的相关方法而是封装成任务提交到NioEventLoop,通过EventExecutor提供的方法判断。此外也提供了快速构建成功的异步结果和失败的异步结果。
- inEventLoop():判断当前线程是否属于EventExecutor关联的内部线程
- next():固定返回当前EventExecutor
- parent():返回对应的EventLoopGroup
- 其他方法用于创建异步结果
- EventLoopGroup:与EventExecutorGroup相似,EventExecutorGroup提供了提交任务相关的方法,而EventLoopGroup提供了注册Channel的方法,此外还提供了一个用于轮询内部EventExecutor的方法
- next():轮询内部EventExecutor
- register():将Channel注册到next()返回的EventExecutor上(基于任务提交的方式)
- OrderedEventExecutor:标记接口没有方法,以有序串行的方式处理提交的任务。
- EventLoop:事件循环,实际上仅仅是覆盖了弗雷德group()方法返回一个更具体地子类EventLoopGroup
NioEventLoop初始化过程
在初始化NioEventLoopGroup时会调用NioEventLoop#newChild()方法创建NioEventLoop。
//NioEventLoopGroup类的newChild方法
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
SelectorProvider selectorProvider = (SelectorProvider) args[0];
SelectStrategyFactory selectStrategyFactory = (SelectStrategyFactory) args[1];
RejectedExecutionHandler rejectedExecutionHandler = (RejectedExecutionHandler) args[2];
EventLoopTaskQueueFactory taskQueueFactory = null;
EventLoopTaskQueueFactory tailTaskQueueFactory = null;
int argsLength = args.length;
if (argsLength > 3) {
taskQueueFactory = (EventLoopTaskQueueFactory) args[3];
}
if (argsLength > 4) {
tailTaskQueueFactory = (EventLoopTaskQueueFactory) args[4];
}
return new NioEventLoop(this, executor, selectorProvider,
selectStrategyFactory.newSelectStrategy(),
rejectedExecutionHandler, taskQueueFactory, tailTaskQueueFactory);
}
- 具体入参如下:
- this:NioEventLoopGroup实例
- executor:ThreadPerTaskExecutor
- selectorProvider:jdk的selectorProvider多路复用器提供商
- selectStrategyFactory.newSelectStrategy():DefaultSelectStrategy,默认的选择策略,通过该选择策略决定NioEventLoop是执行外部提交任务还是监听Channel事件
- rejectedExecutionHandler:当使用有界队列暂存任务时,如果队列已满使用该处理器来决定如何处理,默认抛出异常
- taskQueueFactory:任务队列,及通过execute/submit提交的任务将保存到该队列
- tailTaskQueueFactory:Netty暂时还没有使用到,设计目的是为了定义一个比taskQueueFactory优先级低的任务队列
- NioEventLoop内部成员变量及静态代码块说明
- DEFAULT_MAX_PENDING_TASKS:任务队列的最大值,默认Integer.MAX_VALUE
- AWAKE=-1,与nextWakeupNanos共同作用,当nextWakeupNanos的值为AWAKE时将通过selector.wakeup将线程从阻塞监听事件中唤醒以执行外部提交任务。此外当AWAKE=-1时表示线程正在运行,外部提交的计划任务将直接放入队列并不会添加唤醒任务唤醒线程,NONE与之对应,select()操作将不设置超时时间
- cancelledKeys:当通道关闭时,对端关闭通道将触发deregister事件,最终将会将Channel从selector中删除,该值统计selector上删除的次数,当达到CLEANUP_INTERVAL=256时将出发selector重新构建selector。注意:取消注册是以提交任务的形式被执行的,因此该值的累计只有在短时间内大量Channel取消时才会有可能,因为所有任务执行完成下次再执行任务时会重置为0
- DISABLE_KEY_SET_OPTIMIZATION:禁用优化键功能,默认false,将使用可以扩容的SelectedSelectionKeySet集合替换Selector内部的selectedKeys和publicSelectedKeys类型
- ioRatio:io时间占比,默认50%
- MIN_PREMATURE_SELECTOR_RETURNS=3,产生空轮询的次数限制,超过将导致多路复用器重建,默认512,当自定义低于3时为3
- needsToSelectAgain:在处理io事件前需要再次执行selectNow()来重新获取事件,当产生通道关闭时为true,仅仅在处理事件期间有用,循环体内会别清空
- provider:SelectorProvider
- selectedKeys:SelectedSelectionKeySet,即优化的键集
- selectNowSupplier:selectNow()的实现,默认使用jdk的selectNow(),此外window/linux均有对应的实现
- selector:Selector,内部使用SelectedSelectionKeySet替代原始的HashSet
- unwrappedSelector:Selector,内部使用原始的HashSet
实例化源码分析
- 构造器没有复杂初始化逻辑,都是简单的赋值操作
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler,
EventLoopTaskQueueFactory taskQueueFactory, EventLoopTaskQueueFactory tailTaskQueueFactory) {
//newTaskQueue()将使用MpscUnboundedArrayQueue缓存任务队列,该队列效率比BlockingQueue高
//继续调用父类构造器
super(parent, executor, false, newTaskQueue(taskQueueFactory), newTaskQueue(tailTaskQueueFactory),
rejectedExecutionHandler);
this.provider = ObjectUtil.checkNotNull(selectorProvider, "selectorProvider");
this.selectStrategy = ObjectUtil.checkNotNull(strategy, "selectStrategy");
//创建selector多路复用器
final SelectorTuple selectorTuple = openSelector();
this.selector = selectorTuple.selector;//键集优化后的多路复用器
this.unwrappedSelector = selectorTuple.unwrappedSelector;//原始多路复用器
}
//父类构造器,addTaskWakesUp=false表示外部添加任务将会通过selector.wakeup唤醒阻塞监听
protected SingleThreadEventLoop(EventLoopGroup parent, Executor executor,
boolean addTaskWakesUp, Queue<Runnable> taskQueue, Queue<Runnable> tailTaskQueue,
RejectedExecutionHandler rejectedExecutionHandler) {
//继续调用父类构造器
super(parent, executor, addTaskWakesUp, taskQueue, rejectedExecutionHandler);
tailTasks = ObjectUtil.checkNotNull(tailTaskQueue, "tailTaskQueue");
}
protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
boolean addTaskWakesUp, Queue<Runnable> taskQueue,
RejectedExecutionHandler rejectedHandler) {
super(parent);//简单的在父类构造器中将parent赋值给成员变量
this.addTaskWakesUp = addTaskWakesUp;
this.maxPendingTasks = DEFAULT_MAX_PENDING_EXECUTOR_TASKS;
//这里的传入的executor是NioEventLoopGroup中传入的ThreadPerTaskExecutor,通过ThreadExecutorMap.apply做了封装
this.executor = ThreadExecutorMap.apply(executor, this);
this.taskQueue = ObjectUtil.checkNotNull(taskQueue, "taskQueue");
this.rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
}
- ThreadExecutorMap.apply(executor, this):该方法调用将传入的ThreadPerTaskExecutor进行了封装,具体如下:
public static Executor apply(final Executor executor, final EventExecutor eventExecutor) {
ObjectUtil.checkNotNull(executor, "executor");
ObjectUtil.checkNotNull(eventExecutor, "eventExecutor");
//返回一个新的Executor,内部还是通过ThreadPerTaskExecutor来执行任务,
//仅仅是通过apply()方法封装了提交的任务
return new Executor() {
@Override
public void execute(final Runnable command) {
executor.execute(apply(command, eventExecutor));
}
};
}
//封装提交任务,在原有任务开头和结尾处增加了额外逻辑
//其目的是为了在任务内部获取到线程关联的NioEventLoop
public static Runnable apply(final Runnable command, final EventExecutor eventExecutor) {
ObjectUtil.checkNotNull(command, "command");
ObjectUtil.checkNotNull(eventExecutor, "eventExecutor");
return new Runnable() {
@Override
public void run() {
//任务开始前将当前NioEventLoop放入本地线程变量中
setCurrentEventExecutor(eventExecutor);
try {
command.run();
} finally {
//任务结束前将当前NioEventLoop从本地线程变量中移除
setCurrentEventExecutor(null);
}
}
};
}
NioEventLoop是如何工作的
在Netty的服务端编程中,通过调用ServerBootstrap#bind()方法在内部将会向boss(NioEventLoop)提交一个注册任务,此时将触发NioEventLoop开启一个线程开始工作,因此将从NioEventLoop#execute()开始分析,execute()方法由父类SingleThreadEventExecutor实现,源码如下:
execute()
public void execute(Runnable task) {
ObjectUtil.checkNotNull(task, "task");
//方法重载
execute(task, !(task instanceof LazyRunnable) && wakesUpForTask(task));
}
private void execute(Runnable task, boolean immediate) {
//判断当前线程是否是NioEventLoop关联线程,提交的注册任务属于主线程因此未false
boolean inEventLoop = inEventLoop();
//任务入队
addTask(task);
if (!inEventLoop) {
//启动线程
startThread();
if (isShutdown()) {
boolean reject = false;
try {
if (removeTask(task)) {
reject = true;
}
} catch (UnsupportedOperationException e) {
// The task queue does not support removal so the best thing we can do is to just move on and
// hope we will be able to pick-up the task before its completely terminated.
// In worst case we will log on termination.
}
if (reject) {
reject();
}
}
}
//addTaskWakesUp = false(构造器),immediate = true;
if (!addTaskWakesUp && immediate) {
//唤醒阻塞监听
wakeup(inEventLoop);
}
}
inEventLoop():判断当前线程是否为NioEventLoop关联线程
提交第一个任务时线程还没有启动,此时为null,因此该判断为false
public boolean inEventLoop() {
return inEventLoop(Thread.currentThread());
}
public boolean inEventLoop(Thread thread) {
return thread == this.thread;
}
addTask():任务入队待执行
protected void addTask(Runnable task) {
ObjectUtil.checkNotNull(task, "task");
//入队失败、执行拒绝逻辑,及构造器如惨重的rejectedExecutionHandler,默认抛出异常
if (!offerTask(task)) {
reject(task);
}
}
startThread():启动NioEventLoop关联线程
private void startThread() {
if (state == ST_NOT_STARTED) {
//cas将NioEventLoop的状态由未开始置为开始状态
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;
//这里的executor属于ThreadPerTaskExecutor,其execute()方法见下方,将启动一个
//FastThreadLocalThread线程来执行该任务(FastThreadLocalThread继承Thread)
executor.execute(new Runnable() {
@Override
public void run() {
//将当前线程赋值给NioEventLoop的关联线程
thread = Thread.currentThread();
if (interrupted) {
thread.interrupt();
}
boolean success = false;
//记录该线程自服务启动后的时间间隔
updateLastExecutionTime();
try {
//即执行NioEventLoop#run(),该方法为无线循环,知道外部发送关闭命令
SingleThreadEventExecutor.this.run();
success = true;
} catch (Throwable t) {
logger.warn("Unexpected exception from an event executor: ", t);
} finally {
//···省略掉关闭逻辑
}
});
}
ThreadPerTaskExecutor#execute():该方法将启动一个线程来执行提交的任务
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默认为DefaultThreadFactory
threadFactory.newThread(command).start();
}
}
public class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolId = new AtomicInteger();
private final AtomicInteger nextId = new AtomicInteger();//线程名id不重名
private final String prefix;//线程名前缀:NioEventLoopGroup-1
private final boolean daemon;//守护线程,默认false
private final int priority;//默认值5
protected final ThreadGroup threadGroup;//所在线程组
···
忽略部分无关代码
···
@Override
public Thread newThread(Runnable r) {
//创建线程
Thread t = newThread(FastThreadLocalRunnable.wrap(r), prefix + nextId.incrementAndGet());
try {
if (t.isDaemon() != daemon) {
t.setDaemon(daemon);
}
if (t.getPriority() != priority) {
t.setPriority(priority);
}
} catch (Exception ignored) {
// Doesn't matter even if failed to set.
}
return t;
}
//创建FastThreadLocalThread,该类继承Thread,优化了ThreadLocal访问速度
protected Thread newThread(Runnable r, String name) {
return new FastThreadLocalThread(threadGroup, r, name);
}
}
NioEventLoop#run():循环阻塞监听Channel事件并执行外部提交任务
至此NioEventLoop关联线程已经启动起来,下面分析run方法
protected void run() {
int selectCnt = 0;
for (;;) {
try {
int strategy;
try {
//计算选择策略,如果有任务则由selectNowSupplier提供的值确定,否则执行阻塞监听
//selectNowSupplier将执行selectNow() >= 0
//第一次循环肯定有任务,因此将跳过select()阻塞去执行任务
//strategy >= 0或=-1(阻塞监听case)
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://任务执行完成后再次循环时将走该分支
//不存在计划任务,curDeadlineNanos = -1
long curDeadlineNanos = nextScheduledTaskDeadlineNanos();
if (curDeadlineNanos == -1L) {
curDeadlineNanos = NONE; // 非超时阻塞、直到事件到达
}
//此处设置无限阻塞后在提价schedule任务时将尝试唤醒
nextWakeupNanos.set(curDeadlineNanos);
try {
if (!hasTasks()) {//最后在检测是否存在任务,之后阻塞监听
strategy = select(curDeadlineNanos);
}
} finally {
// 意味着当前正在执行任务,因此外部任务到达时无需唤醒
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
rebuildSelector0();
selectCnt = 0;
handleLoopException(e);
continue;
}
selectCnt++;//阻塞监听次数+1;
cancelledKeys = 0;//重置通道关闭累计值
needsToSelectAgain = false;//重置需要再次selectNow()
final int ioRatio = this.ioRatio;
boolean ranTasks;
if (ioRatio == 100) {
try {
//io占比100%时如果存在Channel事件处理事件
if (strategy > 0) {
processSelectedKeys();
}
} finally {
// 执行所有任务
ranTasks = runAllTasks();
}
} else if (strategy > 0) {
final long ioStartTime = System.nanoTime();
try {
//处理io事件
processSelectedKeys();
} finally {
// 计算io时间
final long ioTime = System.nanoTime() - ioStartTime;
//根据ioRatio计算任务应该执行的时间
ranTasks = runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
} else {
//执行时间为0,将最多执行64个任务后不在执行后续任务
ranTasks = runAllTasks(0);
}
//每次循环只要执行了任务或io事件,则selectCnt重置
if (ranTasks || strategy > 0) {
//如果空轮询了3次以上后不再空轮询则重置selectCnt
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;//重置
//如果由于中断唤醒导致空轮询则重置清零则重置selectCnt
//否则如果空轮询超过512次则认为产生jdk Selector空轮询bug,重新构建Selector并充值selectCnt
} else if (unexpectedSelectorWakeup(selectCnt)) { // Unexpected wakeup (unusual case)
selectCnt = 0;
}
} 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 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 e;
} catch (Throwable t) {
handleLoopException(t);
}
}
}
}
processSelectedKeys():遍历Channel就绪事件
如果在遍历期间发生了大量Channel关闭事件(超过256)则清除未处理的通道事件并从新selectNow()获取
private void processSelectedKeys() {
if (selectedKeys != null) {
processSelectedKeysOptimized();//默认开启select()优化,走该逻辑
} else {
processSelectedKeysPlain(selector.selectedKeys());
}
}
private void processSelectedKeysOptimized() {
//遍历每一个SelectedKey
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
selectedKeys.keys[i] = null;
final Object a = k.attachment();
//selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
//注册的时候附加的是AbstractNioChannel
if (a instanceof AbstractNioChannel) {
//处理通道就绪事件
processSelectedKey(k, (AbstractNioChannel) a);
} else {
//如果注册附加的是NioTask,则后面讲无法获取到AbstractNioChannel
//只能固定的执行task定义的内容,目前没有发现有具体实现类
@SuppressWarnings("unchecked")
NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
processSelectedKey(k, task);
}
//如果在本循环期间发生了很多(超过256)Channel close事件,则needsToSelectAgain将被值为true
//进行selectNow()更新事件
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
//清空未处理的Channel事件重新selectNow()获取,防止
selectedKeys.reset(i + 1);
//重新selectNow()
selectAgain();
i = -1;
}
}
}
processSelectedKey():处理具体Channel事件,区分类型:OP_READ,OP_WIRTE_OPCONNECT,OP_ACCEPT
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) {
// 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.
//确保通道绑定了具体的EventLoop
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) {
// close the channel if the key is not valid anymore
//关闭Channel
unsafe.close(unsafe.voidPromise());
}
return;
}
try {
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.
//处理Channel上的链接事件,该事件仅在客户端才会发生,当客户端执行Channel.connect()后后监听
//CONNECT事件,监听到该时间后调用finishConnect()完成连接,否则如果connect()之后直接执行
//read或write操作的话可能会抛出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);
//jdk nio的finishConnect()将返回boolean值确定是否连接完成,unsafe封装了该操作,如果
//到此还是false的话将抛出异常
unsafe.finishConnect();
}
// Process OP_WRITE first as we may be able to write some queued buffers and so free memory.
//处理Channel上的写事件,将Netty定义的写缓冲区上的数据flush到对端
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
//处理Channel上的读请求和链接请求,事实上读请求和连接请求都是从通道中获取数据,后者获取到channel数据而已
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
}
}
到此NioEventLoop的主流程已经结束,整理一下期间的一些方法的说明如下:
- run() -> nextScheduledTaskDeadlineNanos():在select()阻塞监听时通过该方法获取计最近的计划任务的执行时间,以便阻塞监听该时间后恢复执行计划任务,如果没有计划任务将直接阻塞监听直至有就绪事件到达
- run() -> runAllTasks()、runAllTasks(long times)、runAllTasks(0):用于执行外部提交任务,无论ioRatio比例iwei多少,都要优先执行Channel事件,runAllTasks()将执行所有任务,runAllTasks(long times)将在截至时间到达后返回,实际上以64个任务为执行单元
- processSelectedKeysOptimized和processSelectedKeysPlain:都是遍历就绪事件执行,区别在于前者由于优化了Selector,需要执行自定义的优化逻辑