Java?IO篇之Reactor?網(wǎng)絡(luò)模型的概念
一、什么是 Reactor 模型:
The reactor design pattern is an event handling pattern for handling service requests delivered concurrently to a service handler by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to the associated request handlers.
Reactor 模式也叫做反應(yīng)器設(shè)計模式,是一種為處理服務(wù)請求并發(fā)提交到一個或者多個服務(wù)處理器的事件設(shè)計模式。當請求抵達后,通過服務(wù)處理器將這些請求采用多路分離的方式分發(fā)給相應(yīng)的請求處理器。Reactor 模式主要由 Reactor 和處理器 Handler 這兩個核心部分組成,如下圖所示,它倆負責的事情如下:
- Reactor:負責監(jiān)聽和分發(fā)事件,事件類型包含連接事件、讀寫事件;
- Handler :負責處理事件,如 read -> 業(yè)務(wù)邏輯 (decode + compute + encode)-> send;
在絕大多數(shù)場景下,處理一個網(wǎng)絡(luò)請求有如下幾個步驟:
① read:從 socket 讀取數(shù)據(jù)。
② decode:解碼,網(wǎng)絡(luò)上的數(shù)據(jù)都是以 byte 的形式進行傳輸?shù)?,要想獲取真正的請求,必需解碼
③ compute:計算,也就是業(yè)務(wù)處理。
④ encode:編碼,網(wǎng)絡(luò)上的數(shù)據(jù)都是以 byte 的形式進行傳輸?shù)模簿褪?socket 只接收 byte,所以必需編碼。
⑤ send:發(fā)送應(yīng)答數(shù)據(jù)

對于Reactor模式來說,每當有一個 Event 輸入到 Server 端時,Service Handler 會將其轉(zhuǎn)發(fā)(dispatch)相對應(yīng)的 Handler 進行處理。Reactor 模型中定義的三種角色:
Reactor:派發(fā)器,負責監(jiān)聽和分配事件,并將事件分派給對應(yīng)的 Handler。新的事件包含連接建立就緒、讀就緒、寫就緒等。
Acceptor:請求連接器,處理客戶端新連接。Reactor 接收到 client 端的連接事件后,會將其轉(zhuǎn)發(fā)給 Acceptor,由 Acceptor 接收 Client 的連接,創(chuàng)建對應(yīng)的 Handler,并向 Reactor 注冊此 Handler。
Handler:請求處理器,負責事件的處理,將自身與事件綁定,執(zhí)行非阻塞讀/寫任務(wù),完成 channel 的讀入,完成處理業(yè)務(wù)邏輯后,負責將結(jié)果寫出 channel??捎觅Y源池來管理。
模型大致如下圖所示:

對于讀/寫請求,Reactor 模型是按照以下流程處理的:
- (1)應(yīng)用程序注冊讀/寫就緒事件和相關(guān)聯(lián)的事件處理器
- (2)事件分離器等待事件的發(fā)生
- (3)當發(fā)生讀/寫就緒事件的時候,事件分離器調(diào)用第一步注冊的事件處理器
二、Reactor 模型的分類:
Reactor 模型中的 Reactor 可以是單個也可以是多個,Handler 同樣可以是單線程也可以是多線程,所以組合的模式大致有如下三種:
- 單 Reactor 單線程模型
- 單 Reactor 多線程模型
- 主從 Reactor 單線程模型
- 主從 Reactor 多線程模型
其中第三種的主從Reactor單線程模型沒什么實際意義,所以下文就著重介紹其他三種模型
1、單 Reactor 單線程模型:
1.1、處理流程:

(1)Reactor 線程通過 select 監(jiān)聽事件,收到事件后通過 Dispatch 進行分發(fā)
(2)如果是連接建立事件,則將事件分發(fā)給 Acceptor,Acceptor 會通過 accept() 方法獲取連接,并創(chuàng)建一個Handler 對象來處理后續(xù)的響應(yīng)事件
(3)如果是IO讀寫事件,則 Reactor 會將該事件交由當前連接的 Handler 來處理
(4)Handler 會完成 read -> 業(yè)務(wù)處理 -> send 的完整業(yè)務(wù)流程
1.2、優(yōu)缺點:
單 Reactor 單線程模型的優(yōu)點在于將所有處理邏輯放在一個線程中實現(xiàn),沒有多線程、進程通信、競爭的問題。但該模型在性能與可靠性方面存在比較嚴重的問題:
- ① 性能:只在代碼上進行組件的區(qū)分,整體操作還是單線程,無法充分利用 CPU 資源,并且 Handler 業(yè)務(wù)處理部分沒有異步,一個 Reactor 既要負責處理連接請求,又要負責處理讀寫請求,一般來說處理連接請求是很快的,但處理讀寫請求時涉及到業(yè)務(wù)邏輯處理,相對慢很多。所以 Reactor 在處理讀寫請求時,其他請求只能等著,容易造成系統(tǒng)的性能瓶頸
- ② 可靠性:一旦 Reactor 線程意外中斷或者進入死循環(huán),會導(dǎo)致整個系統(tǒng)通信模塊不可用,不能接收和處理外部消息,造成節(jié)點故障
所以該單Reactor單進程模型不適用于計算密集型的場景,只適用于業(yè)務(wù)處理非常快速的場景。Redis的線程模型就是基于單 Reactor 單線程模型實現(xiàn)的,因為 Redis 業(yè)務(wù)處理主要是在內(nèi)存中完成,操作的速度是很快的,性能瓶頸不在 CPU 上,所以 Redis 對于命令的處理是單進程的。
2、單 Reactor 多線程模型:
為了解決單Reactor單線程模型存在的性能問題,就演進出了單 Reactor 多線程模型,該模型在事件處理器部分采用了多線程(線程池)
2.1、處理流程:

(1)Reactor 線程通過 select 監(jiān)聽事件,收到事件后通過 Dispatch 進行分發(fā)
(2)如果是連接建立事件,則將事件分發(fā)給 Acceptor,Acceptor 會通過 accept() 方法獲取連接,并創(chuàng)建一個Handler 對象來處理后續(xù)的響應(yīng)事件
(3)如果是IO讀寫事件,則 Reactor 會將該事件交由當前連接對應(yīng)的 Handler 來處理
(4)與單Reactor單線程不同的是,Handler 不再做具體業(yè)務(wù)處理,只負責接收和響應(yīng)事件,通過 read 接收數(shù)據(jù)后,將數(shù)據(jù)發(fā)送給后面的 Worker 線程池進行業(yè)務(wù)處理。
(5)Worker 線程池再分配線程進行業(yè)務(wù)處理,完成后將響應(yīng)結(jié)果發(fā)給 Handler 進行處理。
(6)Handler 收到響應(yīng)結(jié)果后通過 send 將響應(yīng)結(jié)果返回給 Client。
2.2、優(yōu)缺點:
相對于第一種模型來說,在處理業(yè)務(wù)邏輯,也就是獲取到 IO讀寫事件之后,交由線程池來處理,Handler 收到響應(yīng)后通過 send 將響應(yīng)結(jié)果返回給客戶端。這樣可以降低 Reactor 的性能開銷,從而更專注的做事件分發(fā)工作了,提升整個應(yīng)用的吞吐,并且 Handler 使用了多線程模式,可以充分利用 CPU 的性能。但是這個模型存在的問題:
(1)Handler 使用多線程模式,自然帶來了多線程競爭資源的開銷,同時涉及共享數(shù)據(jù)的互斥和保護機制,實現(xiàn)比較復(fù)雜
(2)單個 Reactor 承擔所有事件的監(jiān)聽、分發(fā)和響應(yīng),對于高并發(fā)場景,容易造成性能瓶頸。
3、主從 Reactor 多線程模型:
單Reactor多線程模型解決了 Handler 單線程的性能問題,但是 Reactor 還是單線程的,對于高并發(fā)場景還是會有性能瓶頸,所以需要將 Reactor 調(diào)整為多線程模式,也就是接下來要介紹的主從 Reactor 多線程模型。主從 Reactor 多線程模型將 Reactor 分成兩部分:
(1)MainReactor:只負責處理連接建立事件,通過 select 監(jiān)聽 server socket,將建立的 socketChannel 指定注冊給 subReactor,通常一個線程就可以了
(2)SubReactor:負責讀寫事件,維護自己的 selector,基于 MainReactor 注冊的 SocketChannel 進行多路分離 IO 讀寫事件,讀寫網(wǎng)絡(luò)數(shù)據(jù),并將業(yè)務(wù)處理交由 worker 線程池來完成。SubReactor 的個數(shù)一般和 CPU 個數(shù)相同
3.1、處理流程:

(1)主線程中的 MainReactor 對象通過 select 監(jiān)聽事件,接收到事件后通過 Dispatch 進行分發(fā),如果事件類型為連接建立事件則分發(fā)給 Acceptor 進行連接建立
連接建立:
① 從主線程池中隨機選擇一個 Reactor 線程作為 Acceptor 線程,用于綁定監(jiān)聽端口,接收客戶端連接
② Acceptor 線程接收客戶端連接請求之后創(chuàng)建新的 SocketChannel,將其注冊到主線程池的其它 Reactor 線程上,由其負責接入認證、IP黑白名單過濾、握手等操作。
③ 步驟② 完成之后,業(yè)務(wù)層的鏈路正式建立,將 SocketChannel 從主線程池的 Reactor 線程的多路復(fù)用器上摘除,重新注冊到 SubReactor 線程池的線程上,并創(chuàng)建一個 Handler 用于處理各種連接事件
(2)如果接收到的不是連接建立事件,則分發(fā)給 SubReactor,SubReactor 調(diào)用當前連接對應(yīng)的 Handler 進行處理
(3)Handler 通過 read 讀取數(shù)據(jù)后,將數(shù)據(jù)分發(fā)給 Worker 線程池進行業(yè)務(wù)處理,Worker 線程池則分配線程進行業(yè)務(wù)處理,完成后將響應(yīng)結(jié)果發(fā)給 Handler
(4)Handler 收到響應(yīng)結(jié)果后通過 send 將響應(yīng)結(jié)果返回給 Client
3.2、優(yōu)缺點:
主從 Reactor 多線程模型的優(yōu)點在于主線程和子線程分工明確,主線程只負責接收新連接,子線程負責完成后續(xù)的業(yè)務(wù)處理,同時主線程和子線程的交互也很簡單,子線程接收主線程的連接后,只管業(yè)務(wù)處理即可,無須關(guān)注主線程,可以直接在子線程將處理結(jié)果發(fā)送給客戶端。
該 Reactor 模型適用于高并發(fā)場景,并且 Netty 網(wǎng)絡(luò)通信框架也是采用這種實現(xiàn)
4、Reactor 優(yōu)缺點:
(1)響應(yīng)快,不必為單個同步時間所阻塞,雖然 Reactor 本身依然是同步的;
(2)可以最大程度的避免復(fù)雜的多線程及同步問題,并且避免了多線程/進程的切換開銷
(3)可擴展性,可以方便地通過增加 Reactor 實例個數(shù)來充分利用 CPU 資源;
(4)可復(fù)用性,Reactor 模型本身與具體事件處理邏輯無關(guān),具有很高的復(fù)用性。
Reacotr 模型是一種非阻塞同步網(wǎng)絡(luò)模型,除此之外,還有一種 Proactor 的異步網(wǎng)絡(luò)模型,對 Proactor 感興趣的讀者可以閱讀這篇文章:https://blog.csdn.net/a745233700/article/details/122390285
參考文章:
徹底搞懂Reactor模型和Proactor模型 - 云+社區(qū) - 騰訊云
【死磕 NIO】— Reactor 模式就一定意味著高性能嗎?
到此這篇關(guān)于Java IO篇之Reactor 網(wǎng)絡(luò)模型的概念的文章就介紹到這了,更多相關(guān)java Reactor 網(wǎng)絡(luò)模型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatis批量插入(insert)數(shù)據(jù)操作
本文給大家分享MyBatis批量插入(insert)數(shù)據(jù)操作知識,非常不錯,具有參考借鑒價值,感興趣的朋友一起學習吧2016-06-06
MyBatis-Plus多表聯(lián)查的實現(xiàn)方法(動態(tài)查詢和靜態(tài)查詢)
本文用示例介紹使用MyBatis-Plus進行多表查詢的方法,包括靜態(tài)查詢和動態(tài)查詢,通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2022-03-03
解決java -jar XXX.jar沒有主清單屬性以及找不到或無法加載主類的問題
在使用Idea打包SpringBoot項目時,可能會遇到“沒有主清單屬性”的錯誤,問題原因是pom文件中缺少配置,未能正確打包成可執(zhí)行的jar,解決方法包括:1. 修改項目結(jié)構(gòu)并重新生成jar;2. 使用Maven插件在pom文件中添加spring-boot-maven-plugin配置2024-09-09
Kafka中的producer攔截器與consumer攔截器詳解
這篇文章主要介紹了Kafka中的producer攔截器與consumer攔截器詳解,Producer 的Interceptor使得用戶在消息發(fā)送前以及Producer回調(diào)邏輯前有機會對消息做 一些定制化需求,比如修改消息等,需要的朋友可以參考下2023-12-12
Java調(diào)用構(gòu)造函數(shù)和方法及使用詳解
在Java編程中,構(gòu)造函數(shù)用于初始化新創(chuàng)建的對象,而方法則用于執(zhí)行對象的行為,構(gòu)造函數(shù)在使用new關(guān)鍵字創(chuàng)建類實例時自動調(diào)用,沒有返回類型,并且名稱與類名相同,本文通過示例詳細介紹了如何在Java中使用構(gòu)造函數(shù)和方法,感興趣的朋友一起看看吧2024-10-10
Java編程刪除鏈表中重復(fù)的節(jié)點問題解決思路及源碼分享
這篇文章主要介紹了Java編程刪除鏈表中重復(fù)的節(jié)點問題解決思路及源碼分享,具有一定參考價值,這里分享給大家,供需要的朋友了解。2017-10-10

