
前言:
在之前的博文中,本人講解了 Netty 的 概念、基本使用、各種機制 以及 核心源碼
那么,在本篇博文中,本人將來講解一個 開發 中,很重要的問題 —— Selector空輪詢 的解決
首先,本人來講解下 什么是 Selector空輪詢:
概念:
Selector空輪詢是NIO的 API 的 bug
在NIO中,使用Selector的 select方法,來 輪詢當前是否有IO事件,
根據 JDK 中對NIO的 api描述
Selector的 select方法 會 一直阻塞,直到 IO事件達到 或 超時但是在某些場景下,select方法 會 直接返回
即使 沒有超時,並且也沒有IO事件到達,也會 直接返回
這就是 Selector空輪詢(epoll bug)
一般發生了 Selector空輪詢,就會 一直 空輪詢,陷入死循環,因此 CPU飆到100%
最終,線上問題頻頻,服務器崩潰!
可怕的是:到目前為止,JDK還是無法完全避免這個bug產生!
因此,所有使用 NIO 的 成熟框架,都會對 這個bug 做出一些解決
現在,本人來講解下,在 Netty 中,Selector空輪詢(epoll bug) 是如何解決的:
Netty 中的 解決方案:
在 Netty 中,Selector空輪詢(epoll bug) 的解決,是在 select方法 中的:
Selector空輪詢 的解決 —— select方法:


我們可以看到,解決邏輯 是:
- 每次調用后,selectCnt + 1
- 若 超時(正常 跳出阻塞),
重置selectCnt的值- 若 未超時(非正常 跳出阻塞),
重新構造一個selector,並 重置selectCnt的值
那么,本人再來講解下,Netty 是如何 重新構造 Selector 的:
重新構造 Selector —— selectRebuildSelector方法:

我們可以看到:
內部只是:
- 重新構造 selector
- 使用 新的selector,進行 阻塞式監聽
那么,我們來看看,rebuildSelector方法 是如何實現的:
rebuildSelector方法:

我們可以看到:
內部只是:
- 調用 rebuildSelector0方法,也就是 真正的 重建Selector邏輯
真正的 重建Selector邏輯 —— rebuildSelector0方法:



我們可以看到:
內部只是:
- 新建一個 Selector
- 將 舊的Selector 上注冊的key,全部 注冊 到 新的Selector 上
- 將 當前Selector 指向 新的Selector
- 關閉 舊的Selector
那么,最后,本人來 總結 下 在 Netty 中,Selector空輪詢(epoll bug) 是怎樣解決的:
總結:
解決邏輯 為:
- 每次調用后,selectCnt + 1
- 若 超時(正常 跳出阻塞),
重置selectCnt的值- 若 未超時(非正常 跳出阻塞),
重新構造一個selector:
- 新建一個 Selector
- 將 舊的Selector 上注冊的key,全部 注冊 到 新的Selector 上
- 關閉 舊的Selector
- 使用 新的selector,進行 阻塞式監聽
,並 重置selectCnt的值
最后,可能同學們會有疑問:
selectCnt 在 rebuild selector時 沒用啊,為什么要維護那個變量呢?
答曰:
系統日志輸出,方便開發人員進行分析優化處理
以上,就是 Netty 解決 Selector空輪詢(epoll bug) 的 核心邏輯

