前面 ,我們分析了NioEventLoop的創建過程,接下來我們開始分析NioEventLoop的啓動和執行邏輯。
Netty版本:4.1.30
啓動
在之前分析 Channel綁定 的文章中,提到過下面這段代碼,先前只講了 channel.bind() 綁定邏輯,跳過了execute() 接口,現在我們以這個爲例,開始分析NioEventLoop的execute()接口,主要邏輯如下:
- 添加任務隊列
- 綁定當前線程到EventLoop上
- 調用EventLoop的run()方法
前面 ,我們分析了NioEventLoop的創建過程,接下來我們開始分析NioEventLoop的啓動和執行邏輯。
啓動在之前分析 Channel綁定 的文章中,提到過下面這段代碼,先前只講了 channel.bind() 綁定邏輯,跳過了execute() 接口,現在我們以這個爲例,開始分析NioEventLoop的execute()接口,主要邏輯如下:
往下追蹤到 SingleThreadEventExecutor 中 execute 接口,如下: 啓動線程接口: 執行往下調用到 NioEventLoop 中的 run 方法,通過無限for循環,主要做以下三件事情:
輪循檢測I/O事件 重新創建一個新的Selector該方法的主要邏輯就是:
處理I/O事件
執行所有任務接下來,我們瞭解一下執行具體Task任務的接口:runAllTasks。在EventLoop中,待執行的任務隊列分爲兩種:一種是普通任務隊列,一種是定時任務隊列。 前面 我們講 EventLoop 創建時提到過NioEventLoop中 taskQueue 的創建,是一個MpscQueue,關於高效率的MpscQueue 後面單獨寫文章進行介紹: 存放定時任務的隊列在 AbstractScheduledEventExecutor 中,成員變量爲 scheduledTaskQueue,代碼如下: Netty存放定時任務隊列爲 DefaultPriorityQueue ,定時任務的封裝對象爲 ScheduledFutureTask ,在隊列中的優先按照它們的截止時間進行排序,其次在按照id進行排序。 再來看看任務的執行邏輯,首先將定時任務取出,聚合到普通任務隊列中,再去for循環運行每個Task。 小結好了,NioEventLoop的原理以及它的 創建 與 啓動執行 流程到這裏就分析完畢了。啓動流程主要流程如下:
問題:
|
private static void doBind0( final ChannelFuture regFuture, final Channel channel, final SocketAddress localAddress, final ChannelPromise promise) { // 通過eventLoop來執行channel綁定的Task channel.eventLoop().execute(new Runnable() { @Override public void run() { if (regFuture.isSuccess()) { // channel綁定 channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); } else { promise.setFailure(regFuture.cause()); } } }); } |
往下追蹤到 SingleThreadEventExecutor 中 execute 接口,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
@Override public void execute(Runnable task) { if (task == null) { throw new NullPointerException("task"); } // 判斷當前運行時線程是否與EventLoop中綁定的線程一致 // 這裏還未綁定Thread,所以先返回false boolean inEventLoop = inEventLoop(); // 將任務添加任務隊列,也就是我們前面講EventLoop創建時候提到的 MpscQueue. addTask(task); if (!inEventLoop) { // 啓動線程 startThread(); if (isShutdown() && removeTask(task)) { reject(); } } if (!addTaskWakesUp && wakesUpForTask(task)) { wakeup(inEventLoop); } } |
啓動線程接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
private void startThread() { // 狀態比較,最開始時state = 1 ,爲true if (state == ST_NOT_STARTED) { // cs操作後,state狀態設置爲 2 if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) { try { // 啓動接口 doStartThread(); } catch (Throwable cause) { STATE_UPDATER.set(this, ST_NOT_STARTED); PlatformDependent.throwException(cause); } } } } // 執行線程啓動方法 private void doStartThread() { // 斷言判斷 SingleThreadEventExecutor 還未綁定 Thread assert thread == null; // executor 執行任務 executor.execute(new Runnable() { @Override public void run() { // 將 SingleThreadEventExecutor(在我們的案例中就是NioEventLoop) 與 當前線程進行綁定 thread = Thread.currentThread(); if (interrupted) { thread.interrupt(); } // 設置狀態爲 false 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 { ... } } }); } |
執行
往下調用到 NioEventLoop 中的 run 方法,通過無限for循環,主要做以下三件事情:
- 輪循I/O事件:select(wakenUp.getAndSet(false))
- 處理I/O事件:processSelectedKeys
- 運行Task任務:runAllTasks
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
@Override protected void run() { for (;;) { try { switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) { case SelectStrategy.CONTINUE: continue; case SelectStrategy.SELECT: // 輪訓檢測I/O事件 // wakenUp爲了標記selector是否是喚醒狀態,每次select操作,都設置爲false,也就是未喚醒狀態。 select(wakenUp.getAndSet(false)); // 'wakenUp.compareAndSet(false, true)' 總是在調用 'selector.wakeup()' 之前進行評估,以減少喚醒的開銷 // (Selector.wakeup() 是非常耗性能的操作.) // 但是,這種方法存在競爭條件。當「wakeup」太早設置爲true時觸發競爭條件 // 在下面兩種情況下,「wakenUp」會過早設置爲true: // 1)Selector 在 'wakenUp.set(false)' 與 'selector.select(...)' 之間被喚醒。(BAD) // 2)Selector 在 'selector.select(...)' 與 'if (wakenUp.get()) { ... }' 之間被喚醒。(OK) // 在第一種情況下,'wakenUp'設置爲true,後面的'selector.select(...)'將立即喚醒。 直到'wakenUp'在下一輪中再次設置爲false,'wakenUp.compareAndSet(false,true)'將失敗,因此任何喚醒選擇器的嘗試也將失敗,從而導致以下'selector.select(。 ..)'呼籲阻止不必要的。 // 要解決這個問題,如果在selector.select(...)操作之後wakenUp立即爲true,我們會再次喚醒selector。 它是低效率的,因爲它喚醒了第一種情況(BAD - 需要喚醒)和第二種情況(OK - 不需要喚醒)的選擇器。 if (wakenUp.get()) { selector.wakeup(); } // fall through default: } cancelledKeys = 0; needsToSelectAgain = false; // ioRatio 表示處理I/O事件與執行具體任務事件之間所耗時間的比值。 // ioRatio 默認爲50 final int ioRatio = this.ioRatio; if (ioRatio == 100) { try { // 處理I/O事件 processSelectedKeys(); } finally { // 處理任務隊列 runAllTasks(); } } else { // 處理IO事件的開始時間 final long ioStartTime = System.nanoTime(); try { // 處理I/O事件 processSelectedKeys(); } finally { // 記錄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); } } } |
輪循檢測I/O事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
private void select(boolean oldWakenUp) throws IOException { Selector selector = this.selector; try { // select操作計數 int selectCnt = 0; // 記錄當前系統時間 long currentTimeNanos = System.nanoTime(); // delayNanos方法用於計算定時任務隊列,最近一個任務的截止時間 // selectDeadLineNanos 表示當前select操作所不能超過的最大截止時間 long selectDeadLineNanos = currentTimeNanos + delayNanos(currentTimeNanos); for (;;) { // 計算超時時間,判斷是否超時 long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L; // 如果 timeoutMillis <= 0, 表示超時,進行一個非阻塞的 select 操作。設置 selectCnt 爲 1. 並終止本次循環。 if (timeoutMillis <= 0) { if (selectCnt == 0) { selector.selectNow(); selectCnt = 1; } break; } // 當wakenUp爲ture時,恰好有task被提交,這個task將無法獲得調用的機會 // Selector#wakeup. 因此,在執行select操作之前,需要再次檢查任務隊列 // 如果不這麼做,這個Task將一直掛起,直到select操作超時 // 如果 pipeline 中存在 IdleStateHandler ,那麼Task將一直掛起直到 空閒超時。 if (hasTasks() && wakenUp.compareAndSet(false, true)) { // 調用非阻塞方法 selector.selectNow(); selectCnt = 1; break; } // 如果當前任務隊列爲空,並且超時時間未到,則進行一個阻塞式的selector操作。timeoutMillis 爲最大的select時間 int selectedKeys = selector.select(timeoutMillis); // 操作計數 +1 selectCnt ++; // 存在以下情況,本次selector則終止 if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) { // - 輪訓到了事件(Selected something,) // - 被用戶喚醒(waken up by user,) // - 已有任務隊列(the task queue has a pending task.) // - 已有定時任務(a scheduled task is ready for processing) break; } if (Thread.interrupted()) { // Thread was interrupted so reset selected keys and break so we not run into a busy loop. // As this is most likely a bug in the handler of the user or it's client library we will // also log it. // // See https://github.com/netty/netty/issues/2426 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(); // 如果time > currentTimeNanos + timeoutMillis(超時時間),則表明已經執行過一次select操作 if (time - TimeUnit.MILLISECONDS.toNanos(timeoutMillis) >= currentTimeNanos) { // timeoutMillis elapsed without anything selected. selectCnt = 1; } // 如果 time <= currentTimeNanos + timeoutMillis,表示觸發了空輪訓 // 如果空輪訓的次數超過 SELECTOR_AUTO_REBUILD_THRESHOLD (512),則重建一個新的selctor,避免空輪訓 else if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 && selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) { // The selector returned prematurely many times in a row. // Rebuild the selector to work around the problem. logger.warn( "Selector.select() returned prematurely {} times in a row; rebuilding Selector {}.", selectCnt, selector); // 重建創建一個新的selector rebuildSelector(); selector = this.selector; // Select again to populate selectedKeys. // 對重建後的selector進行一次非阻塞調用,用於獲取最新的selectedKeys selector.selectNow(); // 設置select計數 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); } // Harmless exception - log anyway } } |
重新創建一個新的Selector
該方法的主要邏輯就是:
- 創建一個新的selector
- 將老的selector上的 selectKey註冊到新的 selector 上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
public void rebuildSelector() { if (!inEventLoop()) { execute(new Runnable() { @Override public void run() { rebuildSelector0(); } }); return; } rebuildSelector0(); } // 重新創建selector private void rebuildSelector0() { // 暫存老的selector final Selector oldSelector = selector; final SelectorTuple newSelectorTuple; if (oldSelector == null) { return; } try { // 創建一個新的 SelectorTuple // openSelector()在之前分析過了 newSelectorTuple = openSelector(); } catch (Exception e) { logger.warn("Failed to create a new Selector.", e); return; } // Register all channels to the new Selector. // 記錄select上註冊的channel數量 int nChannels = 0; // 遍歷老的 selector 上的 SelectionKey for (SelectionKey key: oldSelector.keys()) { // 獲取 attachment,這裏的attachment就是我們前面在講 Netty Channel註冊時,select會將channel賦值到 attachment 變量上。 // 獲取老的selector上註冊的channel Object a = key.attachment(); try { if (!key.isValid() || key.channel().keyFor(newSelectorTuple.unwrappedSelector) != null) { continue; } // 獲取興趣集 int interestOps = key.interestOps(); // 取消 SelectionKey key.cancel(); // 將老的興趣集重新註冊到前面新創建的selector上 SelectionKey newKey = key.channel().register(newSelectorTuple.unwrappedSelector, interestOps, a); if (a instanceof AbstractNioChannel) { // Update SelectionKey ((AbstractNioChannel) a).selectionKey = newKey; } // nChannels計數 + 1 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 selector = newSelectorTuple.selector; // 設置新的 unwrappedSelector unwrappedSelector = newSelectorTuple.unwrappedSelector; try { // time to close the old selector as everything else is registered to the new one // 關閉老的seleclor oldSelector.close(); } catch (Throwable t) { if (logger.isWarnEnabled()) { logger.warn("Failed to close the old Selector.", t); } } if (logger.isInfoEnabled()) { logger.info("Migrated " + nChannels + " channel(s) to the new Selector."); } } |
處理I/O事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
private void processSelectedKeysOptimized() { 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 // 設置爲null,有利於GC回收 selectedKeys.keys[i] = null; // 獲取 SelectionKey 中的 attachment, 我們這裏就是 NioChannel final Object a = k.attachment(); if (a instanceof AbstractNioChannel) { // 處理 SelectedKey processSelectedKey(k, (AbstractNioChannel) a); } else { @SuppressWarnings("unchecked") NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a; processSelectedKey(k, task); } 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 selectedKeys.reset(i + 1); selectAgain(); i = -1; } } } // 處理 SelectedKey private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) { // 獲取Netty Channel中的 NioUnsafe 對象,用於後面的IO操作 final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe(); // 判斷 SelectedKey 的有效性,如果無效,則直接返回並關閉channel 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. 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 || eventLoop == null) { return; } // close the channel if the key is not valid anymore // 關閉channel unsafe.close(unsafe.voidPromise()); return; } try { // 獲取 SelectionKey 中所有準備就緒的操作集 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. // 在調用處理READ與WRITE事件之間,先調用finishConnect()接口,避免異常 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(); } // Process OP_WRITE first as we may be able to write some queued buffers and so free memory. // 處理 WRITE 事件 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 // 處理 ACCEPT 與 READ 事件 // 如果當前的EventLoop是WorkGroup,則表示有 READ 事件 // 如果當前的EventLoop是BossGroup,則表示有 ACCEPT 事件,有新連接進來了 if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) { // 讀取數據 unsafe.read(); } } catch (CancelledKeyException ignored) { unsafe.close(unsafe.voidPromise()); } } |
關於
unsafe.read()
的分析,請看 後文
執行所有任務
接下來,我們瞭解一下執行具體Task任務的接口:runAllTasks。在EventLoop中,待執行的任務隊列分爲兩種:一種是普通任務隊列,一種是定時任務隊列。
前面 我們講 EventLoop 創建時提到過NioEventLoop中 taskQueue 的創建,是一個MpscQueue,關於高效率的MpscQueue 後面單獨寫文章進行介紹:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
public abstract class SingleThreadEventExecutor extends AbstractScheduledEventExecutor implements OrderedEventExecutor { ... // 存放普通任務的隊列 private final Queue<Runnable> taskQueue; ... protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor, boolean addTaskWakesUp, int maxPendingTasks, RejectedExecutionHandler rejectedHandler) { super(parent); this.addTaskWakesUp = addTaskWakesUp; this.maxPendingTasks = Math.max(16, maxPendingTasks); this.executor = ObjectUtil.checkNotNull(executor, "executor"); // 創建TaskQueue taskQueue = newTaskQueue(this.maxPendingTasks); rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler"); } ... } public final class NioEventLoop extends SingleThreadEventLoop { ... // NioEventLoop 創建TaskQueue隊列 @Override protected Queue<Runnable> newTaskQueue(int maxPendingTasks) { // This event loop never calls takeTask() return maxPendingTasks == Integer.MAX_VALUE ? PlatformDependent.<Runnable>newMpscQueue() : PlatformDependent.<Runnable>newMpscQueue(maxPendingTasks); } ... } |
存放定時任務的隊列在 AbstractScheduledEventExecutor 中,成員變量爲 scheduledTaskQueue,代碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
public abstract class AbstractScheduledEventExecutor extends AbstractEventExecutor { // 優先級隊列的比較器 private static final Comparator<ScheduledFutureTask<?>> SCHEDULED_FUTURE_TASK_COMPARATOR = new Comparator<ScheduledFutureTask<?>>() { @Override public int compare(ScheduledFutureTask<?> o1, ScheduledFutureTask<?> o2) { return o1.compareTo(o2); } }; // 存放定時任務的優先級隊列 PriorityQueue<ScheduledFutureTask<?>> scheduledTaskQueue; // 創建定時任務隊列 PriorityQueue<ScheduledFutureTask<?>> scheduledTaskQueue() { if (scheduledTaskQueue == null) { scheduledTaskQueue = new DefaultPriorityQueue<ScheduledFutureTask<?>>( SCHEDULED_FUTURE_TASK_COMPARATOR, // Use same initial capacity as java.util.PriorityQueue 11); } return scheduledTaskQueue; } // 保存定時任務 @Override public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { ObjectUtil.checkNotNull(command, "command"); ObjectUtil.checkNotNull(unit, "unit"); if (delay < 0) { delay = 0; } validateScheduled0(delay, unit); return schedule(new ScheduledFutureTask<Void>( this, command, null, ScheduledFutureTask.deadlineNanos(unit.toNanos(delay)))); } // 保存定時任務 @Override public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { ObjectUtil.checkNotNull(callable, "callable"); ObjectUtil.checkNotNull(unit, "unit"); if (delay < 0) { delay = 0; } validateScheduled0(delay, unit); return schedule(new ScheduledFutureTask<V>( this, callable, ScheduledFutureTask.deadlineNanos(unit.toNanos(delay)))); } // 保存定時任務 <V> ScheduledFuture<V> schedule(final ScheduledFutureTask<V> task) { // 判斷是否爲當前線程 if (inEventLoop()) { // 添加定時任務隊列 scheduledTaskQueue().add(task); } else { execute(new Runnable() { @Override public void run() { // 添加定時任務隊列 scheduledTaskQueue().add(task); } }); } return task; } } |
Netty存放定時任務隊列爲 DefaultPriorityQueue ,定時任務的封裝對象爲 ScheduledFutureTask ,在隊列中的優先按照它們的截止時間進行排序,其次在按照id進行排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
final class ScheduledFutureTask<V> extends PromiseTask<V> implements ScheduledFuture<V>, PriorityQueueNode { ... // 比較 ScheduledFutureTask 之間的排序 @Override public int compareTo(Delayed o) { if (this == o) { return 0; } ScheduledFutureTask<?> that = (ScheduledFutureTask<?>) o; long d = deadlineNanos() - that.deadlineNanos(); if (d < 0) { return -1; } else if (d > 0) { return 1; } else if (id < that.id) { return -1; } else if (id == that.id) { throw new Error(); } else { return 1; } } ... } |
再來看看任務的執行邏輯,首先將定時任務取出,聚合到普通任務隊列中,再去for循環運行每個Task。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
protected boolean runAllTasks(long timeoutNanos) { // 將定時任務從定時隊列中取出,放入普通隊列中 fetchFromScheduledTaskQueue(); // 從隊列中取出任務 Runnable task = pollTask(); if (task == null) { afterRunningAllTasks(); return false; } // 計算任務執行的最大超時時間 final long deadline = ScheduledFutureTask.nanoTime() + timeoutNanos; // 任務計數 long runTasks = 0; // 最近一次任務執行的時間 long lastExecutionTime; for (;;) { // 執行任務 safeExecute(task); // 任務計數 +1 runTasks ++; // Check timeout every 64 tasks because nanoTime() is relatively expensive. // XXX: Hard-coded value - will make it configurable if it is really a problem. // 由於nanoTime() 是非常好性能的操作,因此每64次就對比一下 定時任務的執行時間與 deadline, // 如果 lastExecutionTime >= deadline,則表示任務超時了,需要中斷退出 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; } // 取出任務 protected Runnable pollTask() { assert inEventLoop(); return pollTaskFrom(taskQueue); } // 從隊列中取出任務 protected static Runnable pollTaskFrom(Queue<Runnable> taskQueue) { for (;;) { Runnable task = taskQueue.poll(); if (task == WAKEUP_TASK) { continue; } return task; } } // 將定時任務從定時隊列中取出,聚合到普通隊列中: private boolean fetchFromScheduledTaskQueue() { // 得到nanoTime = 當前時間 - ScheduledFutureTask的START_TIME(開始時間) long nanoTime = AbstractScheduledEventExecutor.nanoTime(); // 獲得截止時間小於nanoTime的定時任務 Runnable scheduledTask = pollScheduledTask(nanoTime); while (scheduledTask != null) { // 將定時任務放入普通隊列中,以備運行 if (!taskQueue.offer(scheduledTask)) { // No space left in the task queue add it back to the scheduledTaskQueue so we pick it up again. // 如果 taskQueue 沒有足夠的空間,導致添加失敗,則將其返回定時任務隊列中 scheduledTaskQueue().add((ScheduledFutureTask<?>) scheduledTask); return false; } scheduledTask = pollScheduledTask(nanoTime); } return true; } // 獲得截止時間小於nanoTime的定時任務 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; } // 如果該定時任務的截止時間 <= nanoTime ,則返回 if (scheduledTask.deadlineNanos() <= nanoTime) { scheduledTaskQueue.remove(); return scheduledTask; } return null; } |
小結
好了,NioEventLoop的原理以及它的 創建 與 啓動執行 流程到這裏就分析完畢了。啓動流程主要流程如下:
- 將待執行的任務添加到任務隊列中
- 將當前線程綁定到EventLoop上
- 輪循I/O事件,在輪循selector過程中,會對JDK的空輪循Bug做一個處理。
- 處理I/O事件。
- 運行Task任務。將定時任務聚合到普通任務隊列中,然後在依次執行隊列中的任務。
問題:
- 默認情況下,netty服務端啓動多少個線程?何時啓動?
- netty是如何解決空輪訓Bug的?
- netty是如何保證串行無鎖化的?