netty NioEventLoop中run()方法執行流程分析

NioEventLoop的run方法的執行過程

在創建線程對象的任務中調用了SingleThreadEventExecutor.this#run()方法,使NioEventLoop開始運行,開始處理任務。

NioEventLoop#run()方法如下所示,可以看到run()方法是一個無限循環,直到NioEventLoop被關閉。

run()方法中的邏輯主要分爲以下三大步驟

  1. select 選擇任務
  2. processSelectedKeys 處理Channel 感興趣的就緒 IO 事件
  3. runAllTasks 運行所有普通任務和定時任務

run()方法的執行流程如下圖所示:

在這裏插入圖片描述
下面對這三大步驟進行分析

@Override
protected void run() {
    for (;;) {
        try {
            switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
                case SelectStrategy.CONTINUE:// 默認實現下,不存在這個情況。
                    continue;
                case SelectStrategy.SELECT:
                    //將wakenUp字段設置爲false,標識當前狀態爲阻塞
                    //調用select查詢任務
                    select(wakenUp.getAndSet(false));
                    if (wakenUp.get()) {
                        selector.wakeup();
                    }
                default:
            }

            cancelledKeys = 0;//已取消的key的數量
            needsToSelectAgain = false;//是否需要再次選擇
            final int ioRatio = this.ioRatio;//IO比率
            if (ioRatio == 100) {
                try {
                    processSelectedKeys();//處理Channel 感興趣的就緒 IO 事件
                } finally {
                    runAllTasks();
                }
            } else {
                final long ioStartTime = System.nanoTime();
                try {
                    //運行所有普通任務和定時任務,不限制時間
                    processSelectedKeys();//Channel 感興趣的就緒 IO 事件
                } finally {
                    final long ioTime = System.nanoTime() - ioStartTime;
                    //運行所有普通任務和定時任務,限制時間
                    runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
                }
            }
        } catch (Throwable t) {
            handleLoopException(t);
        }
        try {
            if (isShuttingDown()) {
                closeAll();
                if (confirmShutdown()) {
                    return;
                }
            }
        } catch (Throwable t) {
            handleLoopException(t);
        }
    }
}

1.1select 選擇任務

selectNowSupplier

調用Selector的selectNow()方法,此方法執行非阻塞的選擇操作。如果自從前一次選擇操作後,沒有通道變成可選擇的,則此方法直接返回零。

private final IntSupplier selectNowSupplier = new IntSupplier() {
    @Override
    public int get() throws Exception {
        return selectNow();
    }
};

int selectNow() throws IOException {
    try {
        return selector.selectNow();
    } finally {
        if (wakenUp.get()) {
            selector.wakeup();
        }
    }
}

hasTasks()

返回當前任務隊列是否不爲空

@Override
protected boolean hasTasks() {
    return super.hasTasks() || !tailTasks.isEmpty();
}
protected boolean hasTasks() {
    assert inEventLoop();
    return !taskQueue.isEmpty();
}

SelectStrategy

NioEventLoop中的SelectStrategy的實現類爲DefaultSelectStrategy,可以看到,DefaultSelectStrategy是一個單例的

final class DefaultSelectStrategy implements SelectStrategy {
    static final SelectStrategy INSTANCE = new DefaultSelectStrategy();
	//私有構造函數,保證單例
    private DefaultSelectStrategy() { }

    @Override
    public int calculateStrategy(IntSupplier selectSupplier, boolean hasTasks) throws Exception {
        //隊列中有任務則調用selectNow返回當前已就緒IO事件的數量,否則繼續select
        return hasTasks ? selectSupplier.get() : SelectStrategy.SELECT;
    }
}

select(boolean oldWakenUp)

select(boolean oldWakenUp)方法中的主要邏輯爲

1、計算定時任務隊列中最近要執行的任務和當前時間的差值(selectDeadLineNanos)

2、判斷任務隊列中是否有任務沒有執行。如果有,則退出select循環,執行任務

3、如果隊列中沒有任務要執行,則調用jdk底層的select(timeoutMillis)方法繼續阻塞

private void select(boolean oldWakenUp) throws IOException {
    Selector selector = this.selector;//JDK原生的Selector
    try {
        int selectCnt = 0;//已輪訓次數
        long currentTimeNanos = System.nanoTime();
        //計算本次輪詢的截止時間,delayNanos方法用於計算定時任務隊列中最近要執行的任務距離當前時間的差值
        long selectDeadLineNanos = currentTimeNanos + delayNanos(currentTimeNanos);

        for (;;) {
            //timeoutMillis爲本次輪詢的超時時間
            //加500000L爲了方便四捨五入
            //除以1000000L是爲了將納秒換算成毫秒
            long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L;
            if (timeoutMillis <= 0) {
                if (selectCnt == 0) {
                    selector.selectNow();
                    selectCnt = 1;
                }
                break;
            }
            //如果任務隊列中有任務
            if (hasTasks() && wakenUp.compareAndSet(false, true)) {
                selector.selectNow();
                selectCnt = 1;//將已輪訓次數重置爲1
                break;
            }
			//執行到這裏表示任務隊列中沒有要執行的任務,故將selector阻塞timeoutMillis毫秒
            int selectedKeys = selector.select(timeoutMillis);
            selectCnt ++;//已輪訓次數加一
			
            /**
            * selectedKeys != 0 表示輪詢到有已經就緒的IO事件
            * wakenUp.get() 表示是否被用戶喚醒
            * hasTasks() 表示普通任務隊列中有未完成的任務
            * hasScheduledTasks() 表示定時任務隊列中有未完成的任務
            * 上述條件任何一個條件爲真,則退出select,準備處理對應任務
            */
            if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) {
                break;
            }
            if (Thread.interrupted()) {
                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();
            //執行時間大於阻塞超時時間,表示正常執行,則將已輪訓次數重置爲1
            //否則則視爲觸發了JDK空輪訓的bug,如果空輪訓的次數大於事先設定的閾值
            //SELECTOR_AUTO_REBUILD_THRESHOLD,則執行rebuildSelector()避免該bug
			//避免jdk空輪訓bug的方法後面展開分析
            if (time - TimeUnit.MILLISECONDS.toNanos(timeoutMillis) >= currentTimeNanos) {
                selectCnt = 1;
            } else if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 &&
                    selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) {
                logger.warn(
                        "Selector.select() returned prematurely {} times in a row; rebuilding Selector {}.",
                        selectCnt, selector);

                rebuildSelector();
                selector = this.selector;

                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);
        }
    }
}

1.2 processSelectedKeys 處理Channel 感興趣的就緒 IO 事件

processSelectedKeys()方法的定義如下:

如果配置io.netty.noKeySetOptimization爲false,則selectedKeys == null,netty對SelectedKeys做了優化。優化內容後面分析。

private void processSelectedKeys() {
    if (selectedKeys != null) {
        processSelectedKeysOptimized();
    } else {
        processSelectedKeysPlain(selector.selectedKeys());
    }
}

如果io.netty.noKeySetOptimization不爲false,即對selectedKeys進行優化

private void processSelectedKeysOptimized() {
    for (int i = 0; i < selectedKeys.size; ++i) {
        final SelectionKey k = selectedKeys.keys[i];
        selectedKeys.keys[i] = null;
        final Object a = k.attachment();
        if (a instanceof AbstractNioChannel) {
            processSelectedKey(k, (AbstractNioChannel) a);
        } else {
            //io.netty.channel.nio.NioTask ,用於自定義 Nio 事件處理接口
            //對於每個 Nio 事件,可以認爲是一個任務( Task )
            @SuppressWarnings("unchecked")
            NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
            processSelectedKey(k, task);
        }

        if (needsToSelectAgain) {
            selectedKeys.reset(i + 1);

            selectAgain();
            i = -1;
        }
    }
}

這裏只分析是AbstractNioChannel子類的情況

private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
    final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();//獲取channel中Unsafe接口的實現類
    // 如果 SelectionKey 是不合法的,則關閉 Channel
    if (!k.isValid()) {
        final EventLoop eventLoop;
        try {
            eventLoop = ch.eventLoop();
        } catch (Throwable ignored) {
            return;
        }
        if (eventLoop != this || eventLoop == null) {
            return;
        }
        unsafe.close(unsafe.voidPromise());
        return;
    }

    try {
        int readyOps = k.readyOps();//獲取就緒事件類型
        if ((readyOps & SelectionKey.OP_CONNECT) != 0) {//客戶端連接事件
            int ops = k.interestOps();
            ops &= ~SelectionKey.OP_CONNECT;
            k.interestOps(ops);

            unsafe.finishConnect();
        }

        if ((readyOps & SelectionKey.OP_WRITE) != 0) {//寫事件
            ch.unsafe().forceFlush();//上面已經獲取到了unsafe,爲什麼還要再獲取一次???
        }
		//讀事件或者ACCEPT事件
        if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
            unsafe.read();
        }
    } catch (CancelledKeyException ignored) {
        unsafe.close(unsafe.voidPromise());
    }
}

1.3 runAllTasks 運行所有普通任務和定時任務

運行任務隊列中所有的任務

protected boolean runAllTasks() {
    assert inEventLoop();//斷言當前線程就是Channel對應的EventLoop中的線程
    boolean fetchedAll;//是否取全部任務
    boolean ranAtLeastOne = false;//是否至少運行了一個

    do {
        fetchedAll = fetchFromScheduledTaskQueue();
        //運行任務隊列中的所有任務
        if (runAllTasksFrom(taskQueue)) {
            ranAtLeastOne = true;
        }
    } while (!fetchedAll); // keep on processing until we fetched all scheduled tasks.

    if (ranAtLeastOne) {
        //記錄當前的時間
        lastExecutionTime = ScheduledFutureTask.nanoTime();
    }
    //運行tailQueue裏的所有任務
    afterRunningAllTasks();
    return ranAtLeastOne;
}
private boolean fetchFromScheduledTaskQueue() {
    //獲取系統當前時間(是一個相對值)
    long nanoTime = AbstractScheduledEventExecutor.nanoTime();
    //獲取deadline<=nanoTime的定時任務
    Runnable scheduledTask  = pollScheduledTask(nanoTime);
    while (scheduledTask != null) {
        //將上面獲取的定時任務添加到任務隊列的隊首
        if (!taskQueue.offer(scheduledTask)) {
            //如果添加失敗則循環添加
            scheduledTaskQueue().add((ScheduledFutureTask<?>) scheduledTask);
            return false;
        }
        scheduledTask  = pollScheduledTask(nanoTime);
    }
    return true;
}

從定時任務隊列中獲取定時任務

protected final Runnable pollScheduledTask(long nanoTime) {
    assert inEventLoop();

    Queue<ScheduledFutureTask<?>> scheduledTaskQueue = this.scheduledTaskQueue;
    ScheduledFutureTask<?> scheduledTask = scheduledTaskQueue == null ? null : scheduledTaskQueue.peek();
    if (scheduledTask == null) {
        return null;
    }

    if (scheduledTask.deadlineNanos() <= nanoTime) {
        scheduledTaskQueue.remove();
        return scheduledTask;
    }
    return null;
}

帶超時時間的版本

protected boolean runAllTasks(long timeoutNanos) {
    fetchFromScheduledTaskQueue();//將定時隊列中需要執行的定時任務添加到任務隊列中
    Runnable task = pollTask();//獲取隊列中隊首的任務
    if (task == null) {
        afterRunningAllTasks();//運行所有tailQueue中的任務
        return false;
    }
	//計算任務執行的超時時間
    final long deadline = ScheduledFutureTask.nanoTime() + timeoutNanos;
    long runTasks = 0;//已經執行的任務的數量
    long lastExecutionTime;
    for (;;) {
        safeExecute(task);//執行隊列中的任務
        runTasks ++;
        //如果執行的任務數量達到64且超時,則停止執行,目的是防止有過多的任務需要執行,從而導致無法處理IO事件
        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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章