NioEventLoop的运行
wakeup
protected void wakeup(boolean inEventLoop) {
// 因为wakeup操作代价较高,避免频繁地调用wakeup,使用cas来减少调用次数
// inEventLoop代表当前是否在eventloop中,如果为true,代表eventloop正在运行,所以不需要wakeup
// wakeUp有延迟生效的特点,如果在调用wakeup的时候,selector并没有因为执行select方法而阻塞,那么这次wakeup会直接使下一次select方法的调用直接返回
if (!inEventLoop && wakenUp.compareAndSet(false, true)) {
selector.wakeup();
}
}
run
一直执行直到eventLoop关闭,每次循环都会执行下面三件事
- Selector.select 等待io事件
- 处理io时间
- 处理普通任务和延时任务
protected void run() {
for (;;) {
try {
// selectStrategy用来控制select行为
//
switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.SELECT:
// 将wakenup标记设置为false,进行阻塞select
select(wakenUp.getAndSet(false));
// 'wakenUp.compareAndSet(false, true)' is always evaluated
// before calling 'selector.wakeup()' to reduce the wake-up
// overhead. (Selector.wakeup() is an expensive operation.)
//
// However, there is a race condition in this approach.
// The race condition is triggered when 'wakenUp' is set to
// true too early.
//
// 'wakenUp' is set to true too early if:
// 1) Selector is waken up between 'wakenUp.set(false)' and
// 'selector.select(...)'. (BAD)
// 2) Selector is waken up between 'selector.select(...)' and
// 'if (wakenUp.get()) { ... }'. (OK)
//
// In the first case, 'wakenUp' is set to true and the
// following 'selector.select(...)' will wake up immediately.
// Until 'wakenUp' is set to false again in the next round,
// 'wakenUp.compareAndSet(false, true)' will fail, and therefore
// any attempt to wake up the Selector will fail, too, causing
// the following 'selector.select(...)' call to block
// unnecessarily.
//
// To fix this problem, we wake up the selector again if wakenUp
// is true immediately after selector.select(...).
// It is inefficient in that it wakes up the selector for both
// the first case (BAD - wake-up required) and the second case
// (OK - no wake-up required).
// 看下面的图了解为什么会有这段代码
// 判断是否调用了wakeUp方法
if (wakenUp.get()) {
selector.wakeup();
}
// fall through
default:
}
cancelledKeys = 0;
needsToSelectAgain = false;
final int ioRatio = this.ioRatio;
if (ioRatio == 100) {
try {
// 处理io事件
processSelectedKeys();
} finally {
// 运行普通任务和定时任务,不会限制时间
// Ensure we always run tasks.
runAllTasks();
}
} else {
final long ioStartTime = System.nanoTime();
try {
// 处理io事件
processSelectedKeys();
} finally {
// Ensure we always run tasks.
// 处理io事件的时间
final long ioTime = System.nanoTime() - ioStartTime;
// 运行普通任务和定时任务,会限制时间
// 使用处理io事件的时间来计算执行普通任务和定时任务的时间
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
}
} catch (Throwable t) {
handleLoopException(t);
}
// Always handle shutdown even if the loop processing threw an exception.
try {
if (isShuttingDown()) {
closeAll();
if (confirmShutdown()) {
return;
}
}
} catch (Throwable t) {
handleLoopException(t);
}
}
}
SelectStrategy
SelectStategy用来控制select的行为
public interface SelectStrategy {
/**
* Indicates a blocking select should follow.
*/
int SELECT = -1;
/**
* Indicates the IO loop should be retried, no blocking select to follow directly.
*/
int CONTINUE = -2;
/**
* The {@link SelectStrategy} can be used to steer the outcome of a potential select
* call.
*
* @param selectSupplier The supplier with the result of a select result.
* @param hasTasks true if tasks are waiting to be processed.
* @return {@link #SELECT} if the next step should be blocking select {@link #CONTINUE} if
* the next step should be to not select but rather jump back to the IO loop and try
* again. Any value >= 0 is treated as an indicator that work needs to be done.
*/
int calculateStrategy(IntSupplier selectSupplier, boolean hasTasks) throws Exception;
}
主要方法是calculateStrategy,返回值是SELECT或者CONTINUE
- SELECT表示进行阻塞select
- CONTINUE代表重试
- 其他值,跳过select阶段,处理io事件
Netty默认提供的是DefaultSelectStrategy
final class DefaultSelectStrategy implements SelectStrategy {
static final SelectStrategy INSTANCE = new DefaultSelectStrategy();
private DefaultSelectStrategy() {
}
@Override
public int calculateStrategy(IntSupplier selectSupplier, boolean hasTasks) throws Exception {
// 如果当前有任务,返回selectSupplier.get的值
// 没有任务,返回SELECT,执行selector.select
return hasTasks ? selectSupplier.get() : SelectStrategy.SELECT;
}
}
NioEventLoop默认使用的IntSupplier代码如下
private final IntSupplier selectNowSupplier = new IntSupplier() {
@Override
public int get() throws Exception {
// 调用NioEventLoop的selectNow方法,返回当前已经就绪的事件个数
return selectNow();
}
};
selectNow
int selectNow() throws IOException {
try {
return selector.selectNow();
} finally {
// restore wakeup state if needed
if (wakenUp.get()) {
selector.wakeup();
}
}
}
select
private void select(boolean oldWakenUp) throws IOException {
Selector selector = this.selector;
try {
// 用来记录连续空循环的次数,当连续空循环的次数达到阈值时,为了避免cpu100%,创建新的selector
int selectCnt = 0;
long currentTimeNanos = System.nanoTime();
// 定时任务的截止时间
long selectDeadLineNanos = currentTimeNanos + delayNanos(currentTimeNanos);
for (;;) {
// <1> 计算select剩余时间,判断定时任务的截止时间是否快到了
long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L