NioEventLoop源碼分析。
EventLoop的本質:內部一個線程,一個有序隊列存儲,線程源源不斷的運行隊列中的任務。
register方法把java-nio的channel註冊到selector上面。
//把JAVA底層Channel註冊到selector上
public void register(final SelectableChannel ch, final int interestOps, final NioTask<?> task) {
//把ch註冊到java底層selector上面,代碼省略了一些安全檢查
try {
ch.register(selector, interestOps, task);
} catch (Exception e) {
throw new EventLoopException("failed to register a channel", e);
}
}
run方法是核心的啓動方法,在父類中,創建好線程啓動以後會調用run方法,在這裏會處理selector的selelct事件和所有的IO事件
@Override
protected void run() {
//這個方法是父類定義的抽象方法,在父類線程創建啓動後調用此方法。
for (;;) {
try {
//如果底層隊列存儲在任務hasTasks會返回true,那麼調用selelctNow返回key的數量,可能爲0
//如果沒有則返回SelectStrategy.SELECT
switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.SELECT:
//查詢selector,如果沒有任務則會在selector上阻塞一會,否則立即返回
select(wakenUp.getAndSet(false));
if (wakenUp.get()) {
selector.wakeup();
}
// fall through
default:
}
cancelledKeys = 0;
needsToSelectAgain = false;
//IO時間的比例
final int ioRatio = this.ioRatio;
//如果是百分百
if (ioRatio == 100) {
try {
//先處理所有key事件
processSelectedKeys();
} finally {
// Ensure we always run tasks.
//然後處理所有IO任務
runAllTasks();
}
} else {
final long ioStartTime = System.nanoTime();
try {
//先處理所有key事件
processSelectedKeys();
} finally {
// Ensure we always run tasks.
//執行一段時間的IO任務
final long ioTime = System.nanoTime() - ioStartTime;
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);
}
}
}
processSelectedKey,處理selector的selelct到的key。
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
if (!k.isValid()) { //key無效
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;
}
//如果ch當中的el不是當前對象則return
if (eventLoop != this || eventLoop == null) {
return;
}
unsafe.close(unsafe.voidPromise());
return;
}
try {
//獲取key的事件類型
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.
//如果是連接事件
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();
}
//如果是write事件,則調用底層代碼寫入邏輯
// Process OP_WRITE first as we may be able to write some queued buffers and so free memory.
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
//如果是讀事件,交給底層邏輯處理
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
}
}