Netty Reactor 模型之主從多線程模型
1. 前言
通過上節的分析,我們知道單 Reactor 多線程模型它的性能瓶頸在于單個 Reactor,本節主要講解如何進行優化單個 Reactor 帶來的性能瓶頸問題。
2. 單 Reactor 性能瓶頸
單 Reactor 主要存在的性能瓶頸如下:
- 壓力問題: 客戶端數量比較多的情況,單個 Reactor 負責監聽和轉發,那么 Reactor 壓力非常的大;
- 單點故障問題: 如果 Reactor 發生故障,則即使后面的 Handler 和 Worker 正常工作,但是整個應用程序無法正常對外提供服務。
3. 如何進行優化
思考:如何解決單 Reactor 性能問題呢?
- 以 Tomcat 作為案例來進行分析:
1.1 問題: 我們平時把項目打包成 war 部署到單個 Tomcat 來進行運行,在并發量很小的情況下是正常運行的,但是一旦并發量達到 1k 以上,單個 Tomcat 就會很吃力了,那怎么辦呢?
1.2 解決: 很簡單,只需要在 Tomcat 前面加 Nginx 做負載轉發,這樣的話,多個 Tomcat 同時對外提供服務,不但整體的性能得到提高,即使其中一個 Tomcat 宕機,但是整個 Tomcat 集群還是能正常對外提供服務。 - 生活中飯館的案例進行說明:
還是以飯館經營模型說明,方便大家更好的理解。
2.1 一個飯館只有一個老板,老板即兼職服務員和廚師的工作,整體效率很低,這就是單 Reactor 單線程模型;
2.2 一個負責迎接客戶、點菜、上菜的服務員(Reactor 線程),幾個廚師負責炒菜(Worker 線程),廚師輕松了,但是服務員依然忙不過來,這就是單 Reactor 多線程模型;
2.3 一個負責迎接在門口迎接小妹妹(好比:Reactor 主線程),幾個專門負責點菜和上菜的服務員(好比:Reactor 從線程),幾個負責超出廚師(Worker 線程),那么每個崗位都會很輕松,并且還能服務更多的客戶進行就餐,這就是主從 Reactor 多線程模型。
其實,Reactor 模型也是類似道理,哪個環節性能存在瓶頸,那么將其功能再細分,并且增加執行數量(集群)即可。
4. 主從多線程模型
架構圖分析:
- 主要分為三個模塊,分別為 Reactor 主線程、Reactor 子線程、Worker 線程池。其中 Reactor 主線程可以對應多個 Reactor 子線程,也就是說,一個 MainReactor 對應多個 SubReactor;
- Reactor 主線程的 MainReactor 對象通過 select 監聽客戶端連接事件,收到事件之后,通過 Acceptor 處理連接事件;
- 當 Acceptor 處理連接事件之后,MainReactor 將連接事件分配給 Reactor 子線程的 SubReactor 進行處理;
- SubReactor 將連接加入到連接隊列進行監聽,并且創建 Handler 處理對應的事件。一旦有新的事件(非連接)則分配給 Handler 進行處理;
- Handler 通過 read () 方法讀取數據,并且分發給 Worker 線程池去做業務處理;
- Worker 線程池分配線程去處理業務,處理完成之后把結果返回給 Handler;
- Handler 收到 Worker 線程返回的結果之后,再通過 send () 方法返回給客戶端。
方案的優點:
- 責任明確,單一功能拆分的更細,Reactor 主線程負責接收請求,不負責處理請求;Reactor 子線程負責處理請求。并發量很高的情況,可以減輕單個 Reactor 的壓力,并且提高處理速度;
- Reactor 子線程只負責讀取數據和響應數據,耗時的業務處理則丟給 Worker 線程池去處理。這種通過把完整任務層層分發下去,每個組件需要處理的內容就會變的很簡單,處理起來效率自然會很高。
方案的缺點:
- 編程復雜度非常的高;
- 即使一個 Reactor 主線程對應多個 Reactor 子線程,Reactor 主線程還是會存在單節點故障問題,不過真實業務場景當中,如果考慮單節點故障問題的話,一般都是通過分布式集群(Netty 集群)的方式去解決,而不是靠單節點的線程模型去解決,這里大家了解一下即可。
總的來說,主從多線程模型是應用比較多的一種線程模型,包括 Nginx 主從 Reactor 多線程模型、Memcached 主從多線程模型、Netty 主從多線程模型等知名開源框架的。
5. 模型對比
Reactor 模型和傳統的 IO 模型對比
傳統 IO 模型 | Reactor 模型 | |
---|---|---|
線程分配 | 為每個客戶端都分配獨立的線程,該線程負責全部的工作(包括:監聽、讀取、處理、響應) | 統一的監聽客戶端請求,并且把功能細分,并且分配給不同的子線程去處理 |
堵塞點 | 在每個子線程的 read () 方法進行堵塞 | 只在 select () 堵塞,select () 是所有客戶端共用的入口點 |
整體性能 | 并發量相對有限 | 可以處理高并發 |
Reactor 的整體優點如下:
- 性能好,Reactor 本身雖然是同步的,但是是非堵塞的,可以快速的響應;
- 擴展性好,可以根據 CPU 的核數來調整 Reactor 的實例個數,充分的利用 CPU 資源;
- 復用性好,它是一種思想,可以靈活的運用到不同的中間件、底層框架上。
三種 Reactor 線程模型對比
單 Reactor 單線程 | 單 Reactor 多線程 | 主從多線程 | |
---|---|---|---|
功能 | 一個線程負責所有業務 | 一個線程服務監聽、事件處理、轉發,多個線程負責邏輯處理 | 一個線程負責監聽,多個線程負責事件處理、轉發,多個線程負責邏輯處理 |
線程 | 一個線程 | 一個線程,一個線程組 | 一個線程,兩個線程組 |
性能 | 低 | 中 | 高 |
高可用 | 否 | 否 | 是 |
6. 小結
通過這幾個小節的講解,相信大家對 Reactor 線程模型都已經有了一定的了解了,其實我們只需要了解這幾種模型的架構思想即可。Reactor 它是一種思想,而并非是 Netty 所特有的,常見的中間件 Nginx、Redis 等底層通訊也都是基于 Reactor 思想去實現。只有把 Reactor 模型理解了,后期在閱讀源碼時才能更好的理解 Netty。