Netty源碼解析 -- 事件循環機制實現原理

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文主要分享Netty中事件循環機制的實現。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"源碼分析基於Netty 4.1"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"EventLoop"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面分享服務端和客戶端啓動過程的文章中說過,Netty通過事件循環機制(EventLoop)處理IO事件和異步任務,簡單來說,就是通過一個死循環,不斷處理當前已發生的IO事件和待處理的異步任務。示例如下"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"while(true) {\n\tprocess(selector.select());\n\n\tprocess(getTask());\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這種事件循環機制也是一種常用的IO事件處理機制,包括Redis,Mysql都使用了類似的機制。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關於異步任務,前面文章說過,EventLoop實現了(jvm)Executor的接口,execute方法可以提供異步任務。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"register,bind,connect等操作,都會提交一個任務給EventLoop處理。如"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"if (eventLoop.inEventLoop()) {\n\tregister0(promise);\t\n} else {\n\teventLoop.execute(new Runnable() {\t\n\t\tpublic void run() {\n\t\t\tregister0(promise);\n\t\t}\n\t});\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面看一下Netty中事件循環機制相關的類。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"EventExecutor,事件執行器,負責處理事件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"EventExecutorGroup維護了一個EventExecutor鏈表,它繼承了ScheduledExecutorService,execute方法通過next方法選擇一個EventExecutor,並調用EventLoop#execute處理事件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(EventExecutor繼承了EventExecutorGroup,可以看做一個特殊的EventExecutorGroup,其execute方法可以提交一個任務任務)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"EventLoop,事件循環器,繼承了EventExecutor,通過循環不斷處理註冊於其上的Channel的IO事件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"EventLoopGroup接口則繼承了EventExecutorGroup,負責調度EventLoop。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SingleThreadEventExecutor實現了EventExecutor,它會創建一個新線程,並在該線程上處理事件,可以理解爲單線程處理器。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"MultithreadEventExecutorGroup實現EventExecutorGroup,可以理解爲多線程處理器(實際上是維護了多個EventExecutor,一個EventExecutor可以理解爲一個線程),newChild方法構造具體的EventExecutor。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"MultithreadEventExecutorGroup可以配置EventExecutor數量,即線程數量。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"EventExecutorChooserFactory.EventExecutorChooser負責選擇一個EventExecutor執行實際操作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"NioEventLoop繼承了SingleThreadEventExecutor,負責處理NIO事件。所以,一個NioEventLoop對象可以看做是一個線程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"NioEventLoop也實現了EventLoop接口,它實現了事件循環機制,是Netty核心類。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"MultithreadEventLoopGroup繼承了MultithreadEventExecutorGroup,並實現了EventLoopGroup,其newChild方法構造具體的EventLoop。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"NioEventLoopGroup#newChild會構建NioEventLoop。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"EventLoop各實現類關係如下"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0c/0c798a130a5a981c40b262af818ce0b1.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"啓動"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SingleThreadEventExecutor關鍵字段"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"private final Queue taskQueue;\t// 待處理異步任務\nprivate volatile Thread thread;\t\t\t\t// EventLoop執行線程,即SingleThreadEventExecutor創建的新線程\nprivate final Executor executor;\t\t\t// java.util.concurrent.Executor,負責創建線程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當我們通過execute方法提交任務時,如果還沒有創建執行新線程,會通過SingleThreadEventExecutor#executor一個新線程,並在新線程中調用run方法(run方法由子類實現,負責實現事件循環機制,新線程是EventLoop真正執行線程)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SingleThreadEventExecutor#execute"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"public void execute(Runnable task) {\n\t...\n\n\tboolean inEventLoop = inEventLoop();\n\t// #1\n\taddTask(task);\n\t// #2\n\tif (!inEventLoop) {\n\t\tstartThread();\n\t\t// #3\n\t\tif (isShutdown()) {\n\t\t\t...\n\t\t}\n\t}\n\t// #4\n\tif (!addTaskWakesUp && wakesUpForTask(task)) {\n\t\twakeup(inEventLoop);\n\t}\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#1"}]},{"type":"text","text":" 添加任務到待處理列表"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#2"}]},{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"inEventLoop方法,判斷當前線程是否爲EventLoop執行線程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"若當前線程非EventLoop執行線程,調用startThread方法啓動一個新的線程,執行run方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏可以理解爲啓動EventLoop。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#3"}]},{"type":"text","text":" 如果當前EventLoop已關閉,拒絕任務"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#4"}]},{"type":"text","text":" 若當前EventLoop線程阻塞正等待IO事件(Selector#select方法),調用wakeup方法喚醒線程執行該新增任務"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"循環機制"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"NioEventLoop#run方法負責實現NIO事件處理機制。"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"protected void run() {\n\tint selectCnt = 0;\n\t// #1\n\tfor (;;) {\n\n\t\t\tint strategy;\n\t\t\t\n\t\t\t\t// #2\n\t\t\t\tstrategy = selectStrategy.calculateStrategy(selectNowSupplier, hasTasks());\n\t\t\t\tswitch (strategy) {\n\t\t\t\tcase SelectStrategy.CONTINUE:\n\t\t\t\t\tcontinue;\n\n\t\t\t\tcase SelectStrategy.BUSY_WAIT:\n\t\t\t\t\t// fall-through to SELECT since the busy-wait is not supported with NIO\n\n\t\t\t\tcase SelectStrategy.SELECT:\n\t\t\t\t\t// #3\n\t\t\t\t\tlong curDeadlineNanos = nextScheduledTaskDeadlineNanos();\n\t\t\t\t\tif (curDeadlineNanos == -1L) {\n\t\t\t\t\t\tcurDeadlineNanos = NONE; // nothing on the calendar\n\t\t\t\t\t}\n\t\t\t\t\tnextWakeupNanos.set(curDeadlineNanos);\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// #4\n\t\t\t\t\t\tif (!hasTasks()) {\n\t\t\t\t\t\t\tstrategy = select(curDeadlineNanos);\n\t\t\t\t\t\t}\n\t\t\t\t\t} finally {\n\t\t\t\t\t\t// #5\n\t\t\t\t\t\tnextWakeupNanos.lazySet(AWAKE);\n\t\t\t\t\t}\n\t\t\t\t\t// fall through\n\t\t\t\tdefault:\n\t\t\t\t}\n\t\t\t\t...\n\t\t\t\n\t\t\t// #6\n\t\t\tselectCnt++;\n\t\t\tcancelledKeys = 0;\n\t\t\tneedsToSelectAgain = false;\n\t\t\tfinal int ioRatio = this.ioRatio;\n\t\t\tboolean ranTasks;\n\t\t\t// #7\n\t\t\tif (ioRatio == 100) {\n\t\t\t\ttry {\n\t\t\t\t\tif (strategy > 0) {\n\t\t\t\t\t\tprocessSelectedKeys();\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\t// Ensure we always run tasks.\n\t\t\t\t\tranTasks = runAllTasks();\n\t\t\t\t}\n\t\t\t} else if (strategy > 0) {\n\t\t\t\tfinal long ioStartTime = System.nanoTime();\n\t\t\t\ttry {\n\t\t\t\t\tprocessSelectedKeys();\n\t\t\t\t} finally {\n\t\t\t\t\t// Ensure we always run tasks.\n\t\t\t\t\tfinal long ioTime = System.nanoTime() - ioStartTime;\n\t\t\t\t\tranTasks = runAllTasks(ioTime * (100 - ioRatio) / ioRatio);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tranTasks = runAllTasks(0); // This will run the minimum number of tasks\n\t\t\t}\n\t\t\t// #8\n\t\t\tif (ranTasks || strategy > 0) {\n\t\t\t\tif (selectCnt > MIN_PREMATURE_SELECTOR_RETURNS && logger.isDebugEnabled()) {\n\t\t\t\t\tlogger.debug(\"Selector.select() returned prematurely {} times in a row for Selector {}.\",\n\t\t\t\t\t\t\tselectCnt - 1, selector);\n\t\t\t\t}\n\t\t\t\tselectCnt = 0;\n\t\t\t} else if (unexpectedSelectorWakeup(selectCnt)) { // Unexpected wakeup (unusual case)\n\t\t\t\tselectCnt = 0;\n\t\t\t}\n\t\t\n\t\t\n\t\t\t// #9\n\t\t\tif (isShuttingDown()) {\n\t\t\t\tcloseAll();\n\t\t\t\tif (confirmShutdown()) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\n\t}\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了版面整潔,這裏刪除了異常處理代碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#1"}]},{"type":"text","text":" 可以看到,這裏通過一個死循環不斷處理IO事件和異步任務。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#2"}]},{"type":"text","text":" 如果當前存在待處理的任務,調用selector.selectNow(),這時會跳出switch語句,往下處理事件和任務,否則返回SelectStrategy.SELECT。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#3"}]},{"type":"text","text":" curDeadlineNanos,計算延遲任務隊列中第一個任務的到期執行時間(即最晚還能延遲多長時間執行),沒有任務則返回-1。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"更新nextWakeupNanos爲阻塞時間。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於頻繁調用(jvm)Selector.wakeup會造成性能消耗,NioEventLoop維護了一個喚醒標識nextWakeupNanos。nextWakeupNanos有三種值"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"NONE -- 執行線程被阻塞;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AWAKE -- 執行線程未阻塞;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其他值 -- 執行線程被超時阻塞,在指定的時間後喚醒;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"NioEventLoop#wakeup方法中,只有"},{"type":"codeinline","content":[{"type":"text","text":"nextWakeupNanos.getAndSet(AWAKE) != AWAKE"}]},{"type":"text","text":"成功才調用selector.wakeup()方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#4"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這時如果還沒有任務加入,則執行select,阻塞線程。select方法返回結果作爲新的strategy。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#5"}]},{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"lazySet方法,設置值之後其他線程在短期內還是可能讀到舊值"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏將nextWakeupNanos設置爲AWAKE,主要是減少wakeup方法中不必要的wakeup操作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以使用lazySet方法也沒有問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#6"}]},{"type":"text","text":" selectCnt增加"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"舊版本的Java NIO在Linux Epoll實現上存在bug,(jvm)Selector.select方法可能在沒有任何就緒事件的情況下返回,導致CPU空轉,利用率飆升到100%。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"於是,Netty計算select方法重複調用次數selectCnt,並在selectCnt大於SELECTOR"},{"type":"text","marks":[{"type":"italic"}],"text":"AUTO"},{"type":"text","text":"REBUILD_THRESHOLD配置(默認512)時,重建selector,從而規避該問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"幸好在JDK6"},{"type":"text","marks":[{"type":"italic"}],"text":"6u4,JDK7"},{"type":"text","text":"b12已修復該Bug。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#7"}]},{"type":"text","text":" processSelectedKeys方法處理IO事件,runAllTasks方法處理任務。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ioRatio表示執行IO事件所佔CPU時間百分比,默認50,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"ioTime * (100 - ioRatio) / ioRatio"}]},{"type":"text","text":",通過ioTime,ioRatio計算處理任務的CPU時間。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#8"}]},{"type":"text","text":" 如果執行了任務或者select方法返回有效值,直接重置selectCnt。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"unexpectedSelectorWakeup方法中會在selectCnt大於SELECTOR"},{"type":"text","marks":[{"type":"italic"}],"text":"AUTO"},{"type":"text","text":"REBUILD_THRESHOLD時重建selector。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#9"}]},{"type":"text","text":" 如果是正在關閉狀態,則要關閉所有的Channel"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"IO事件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面看一下Eventloop中如何處理IO事件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"NioEventLoop關鍵字段"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"Selector unwrappedSelector;\t\t\t\t// JVM中的Selector\nSelector selector;\t\t\t\t\t\t// 優化後的SelectedSelectionKeySetSelector\nSelectedSelectionKeySet selectedKeys;\t// 對(jvm)Selector#selectedKeys進行優化"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SelectedSelectionKeySetSelector每次調用select前都清除SelectedSelectionKeySet"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SelectedSelectionKeySet使用數組代替原Selector的中的HashSet,提高性能。數組默認大小爲1024,不夠用時擴展爲原大小的2倍。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"NioEventLoop#構造方法 -> NioEventLoop#openSelector"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"private SelectorTuple openSelector() {\n final Selector unwrappedSelector;\n try {\n\t\t// #1\n unwrappedSelector = provider.openSelector();\n } catch (IOException e) {\n throw new ChannelException(\"failed to open a new selector\", e);\n }\n\n if (DISABLE_KEY_SET_OPTIMIZATION) {\n return new SelectorTuple(unwrappedSelector);\n }\n\n ...\n\n final Class> selectorImplClass = (Class>) maybeSelectorImplClass;\n final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();\n\n Object maybeException = AccessController.doPrivileged(new PrivilegedAction() {\n \n public Object run() {\n try {\n\t\t\t\t// #2\n Field selectedKeysField = selectorImplClass.getDeclaredField(\"selectedKeys\");\n Field publicSelectedKeysField = selectorImplClass.getDeclaredField(\"publicSelectedKeys\");\n\n ...\n\n selectedKeysField.set(unwrappedSelector, selectedKeySet);\n publicSelectedKeysField.set(unwrappedSelector, selectedKeySet);\n return null;\n } ...\n }\n });\n\n ...\n selectedKeys = selectedKeySet;\n logger.trace(\"instrumented a special java.util.Set into: {}\", unwrappedSelector);\n\t// #3\n return new SelectorTuple(unwrappedSelector,\n new SelectedSelectionKeySetSelector(unwrappedSelector, selectedKeySet));\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#1"}]},{"type":"text","text":" 通過(jvm)SelectorProvider打開一個Selector"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#2"}]},{"type":"text","text":" 構造了selectedKeySet,並通過反射將該對象設置到Selector的selectedKeys,publicSelectedKeys屬性中,這樣Selector監聽到的事件就會存儲到selectedKeySet。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#3"}]},{"type":"text","text":" 構造了SelectedSelectionKeySetSelector對象"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"NioEventLoop#select負責阻塞線程,等待IO事件"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"private int select(long deadlineNanos) throws IOException {\n\t// #1\n\tif (deadlineNanos == NONE) {\n\t\treturn selector.select();\n\t}\n\n\t// #2\n\tlong timeoutMillis = deadlineToDelayNanos(deadlineNanos + 995000L) / 1000000L;\n\treturn timeoutMillis <= 0 ? selector.selectNow() : selector.select(timeoutMillis);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#1"}]},{"type":"text","text":" 一直阻塞,知道發生IO事件或加入了新任務"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#2"}]},{"type":"text","text":" 計算阻塞時間,在原阻塞時間加上995微秒後轉化爲毫秒。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果原阻塞時間在5微秒內,就不阻塞了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"IO事件的處理流程爲"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"NioEventLoop#processSelectedKeys -> (沒有禁用SelectedSelectionKeySet)processSelectedKeysOptimized -> processSelectedKey"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {\n\tfinal AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();\n\t...\n\n\ttry {\n\t\tint readyOps = k.readyOps();\n\t\t// #1\n\t\tif ((readyOps & SelectionKey.OP_CONNECT) != 0) {\n\t\t\tint ops = k.interestOps();\n\t\t\tops &= ~SelectionKey.OP_CONNECT;\n\t\t\tk.interestOps(ops);\n\n\t\t\tunsafe.finishConnect();\n\t\t}\n\n\t\t// #2\n\t\tif ((readyOps & SelectionKey.OP_WRITE) != 0) {\n\t\t\t// Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write\n\t\t\tch.unsafe().forceFlush();\n\t\t}\n\n\t\t// Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead\n\t\t// to a spin loop\n\t\t// #3\n\t\tif ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {\n\t\t\tunsafe.read();\n\t\t}\n\t} catch (CancelledKeyException ignored) {\n\t\tunsafe.close(unsafe.voidPromise());\n\t}\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#1"}]},{"type":"text","text":" 處理OP_CONNECT"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"移除關注事件OP_CONNECT,否則Selector.select(..)將不斷返回"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面分享客戶端啓動過程的文章說過了,這裏會調用AbstractNioUnsafe#finishConnect,完成客戶端Connect操作,可回顧《客戶端啓動過程解析》。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#2"}]},{"type":"text","text":" 先處理OP_WRITE事件,能夠儘早寫入數據釋放內存,這裏涉及flush操作,後面有文章解析。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#3"}]},{"type":"text","text":" 處理OP"},{"type":"text","marks":[{"type":"italic"}],"text":"READ或OP"},{"type":"text","text":"ACCEPT事件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於ServerChannel,這裏會調用NioMessageUnsafe#read,處理OP_ACCEPT事件,可回顧《客戶端啓動過程解析》。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於SocketChannel,這裏會調用NioByteUnsafe#read,進行讀寫操作,後面有文章解析。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"異步任務"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面看一下Eventloop中如何處理異步任務。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"run方法"},{"type":"codeinline","content":[{"type":"text","text":"#4"}]},{"type":"text","text":"步驟 -> SingleThreadEventExecutor#runAllTasks"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"protected boolean runAllTasks(long timeoutNanos) {\n // #1\n fetchFromScheduledTaskQueue();\n Runnable task = pollTask();\n if (task == null) {\n afterRunningAllTasks();\n return false;\n }\n\n final long deadline = timeoutNanos > 0 ? ScheduledFutureTask.nanoTime() + timeoutNanos : 0;\n long runTasks = 0;\n long lastExecutionTime;\n for (;;) {\n \t// #2\n safeExecute(task);\n\n runTasks ++;\n\n // #3\n if ((runTasks & 0x3F) == 0) {\n lastExecutionTime = ScheduledFutureTask.nanoTime();\n if (lastExecutionTime >= deadline) {\n break;\n }\n }\n // #4\n task = pollTask();\n if (task == null) {\n lastExecutionTime = ScheduledFutureTask.nanoTime();\n break;\n }\n }\n // #5\n afterRunningAllTasks();\n this.lastExecutionTime = lastExecutionTime;\n return true;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#1"}]},{"type":"text","text":" AbstractScheduledEventExecutor#scheduledTaskQueue中存放的是定時任務,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SingleThreadEventExecutor#taskQueue中存放的是待處理的任務。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"fetchFromScheduledTaskQueue方法會獲取已到期的定時任務,移動到SingleThreadEventExecutor#taskQueue。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#2"}]},{"type":"text","text":" 執行獲取的任務"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#3"}]},{"type":"text","text":" 每個64個任務檢查一次是否超時,因爲nanoTime()方法也是一個相對昂貴的操作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#4"}]},{"type":"text","text":" 取下一個任務,繼續處理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#5"}]},{"type":"text","text":" 預留的擴展方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"NioEventLoop在Netty 4.1版本被優化,代碼做了較大改動,刪除了原來的wakeup標誌,改用nextWakeupNanos,代碼更清晰。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"請參考 -- "},{"type":"link","attrs":{"href":"https://github.com/netty/netty/commit/e208e96f128b43cbd41654c94eacfbce732fa62d#diff-776b30ff5a811300c798fda3b79e7d09c6e36204adfcc9130a17b495d5decc59","title":""},"content":[{"type":"text","text":"Clean up NioEventLoop"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Netty是由事件驅動的,服務端register,bind,客戶端connect等操作都是提交異步任務給EventLoop處理的"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":",而Accept,Read/Writ,Connect等IO事件都都需要EventLoop的處理。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大家可以結合前面分析服務端和客戶端啓動過程的文章,理解EventLoop是如何驅動Netty工作的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果您覺得本文不錯,歡迎關注我的微信公衆號,您的關注是我堅持的動力!"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/da/da6c5c8363dc2a6fc148bc2eab39d883.jpeg","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章