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è)計(jì)模式,是一種為處理服務(wù)請(qǐng)求并發(fā)提交到一個(gè)或者多個(gè)服務(wù)處理器的事件設(shè)計(jì)模式。當(dāng)請(qǐng)求抵達(dá)后,通過(guò)服務(wù)處理器將這些請(qǐng)求采用多路分離的方式分發(fā)給相應(yīng)的請(qǐng)求處理器。Reactor 模式主要由 Reactor 和處理器 Handler 這兩個(gè)核心部分組成,如下圖所示,它倆負(fù)責(zé)的事情如下:
- Reactor:負(fù)責(zé)監(jiān)聽和分發(fā)事件,事件類型包含連接事件、讀寫事件;
- Handler :負(fù)責(zé)處理事件,如 read -> 業(yè)務(wù)邏輯 (decode + compute + encode)-> send;
在絕大多數(shù)場(chǎng)景下,處理一個(gè)網(wǎng)絡(luò)請(qǐng)求有如下幾個(gè)步驟:
① read:從 socket 讀取數(shù)據(jù)。
② decode:解碼,網(wǎng)絡(luò)上的數(shù)據(jù)都是以 byte 的形式進(jìn)行傳輸?shù)模氆@取真正的請(qǐng)求,必需解碼
③ compute:計(jì)算,也就是業(yè)務(wù)處理。
④ encode:編碼,網(wǎng)絡(luò)上的數(shù)據(jù)都是以 byte 的形式進(jìn)行傳輸?shù)?,也就?socket 只接收 byte,所以必需編碼。
⑤ send:發(fā)送應(yīng)答數(shù)據(jù)
對(duì)于Reactor模式來(lái)說(shuō),每當(dāng)有一個(gè) Event 輸入到 Server 端時(shí),Service Handler 會(huì)將其轉(zhuǎn)發(fā)(dispatch)相對(duì)應(yīng)的 Handler 進(jìn)行處理。Reactor 模型中定義的三種角色:
Reactor:派發(fā)器,負(fù)責(zé)監(jiān)聽和分配事件,并將事件分派給對(duì)應(yīng)的 Handler。新的事件包含連接建立就緒、讀就緒、寫就緒等。
Acceptor:請(qǐng)求連接器,處理客戶端新連接。Reactor 接收到 client 端的連接事件后,會(huì)將其轉(zhuǎn)發(fā)給 Acceptor,由 Acceptor 接收 Client 的連接,創(chuàng)建對(duì)應(yīng)的 Handler,并向 Reactor 注冊(cè)此 Handler。
Handler:請(qǐng)求處理器,負(fù)責(zé)事件的處理,將自身與事件綁定,執(zhí)行非阻塞讀/寫任務(wù),完成 channel 的讀入,完成處理業(yè)務(wù)邏輯后,負(fù)責(zé)將結(jié)果寫出 channel??捎觅Y源池來(lái)管理。
模型大致如下圖所示:
對(duì)于讀/寫請(qǐng)求,Reactor 模型是按照以下流程處理的:
- (1)應(yīng)用程序注冊(cè)讀/寫就緒事件和相關(guān)聯(lián)的事件處理器
- (2)事件分離器等待事件的發(fā)生
- (3)當(dāng)發(fā)生讀/寫就緒事件的時(shí)候,事件分離器調(diào)用第一步注冊(cè)的事件處理器
二、Reactor 模型的分類:
Reactor 模型中的 Reactor 可以是單個(gè)也可以是多個(gè),Handler 同樣可以是單線程也可以是多線程,所以組合的模式大致有如下三種:
- 單 Reactor 單線程模型
- 單 Reactor 多線程模型
- 主從 Reactor 單線程模型
- 主從 Reactor 多線程模型
其中第三種的主從Reactor單線程模型沒什么實(shí)際意義,所以下文就著重介紹其他三種模型
1、單 Reactor 單線程模型:
1.1、處理流程:
(1)Reactor 線程通過(guò) select 監(jiān)聽事件,收到事件后通過(guò) Dispatch 進(jìn)行分發(fā)
(2)如果是連接建立事件,則將事件分發(fā)給 Acceptor,Acceptor 會(huì)通過(guò) accept() 方法獲取連接,并創(chuàng)建一個(gè)Handler 對(duì)象來(lái)處理后續(xù)的響應(yīng)事件
(3)如果是IO讀寫事件,則 Reactor 會(huì)將該事件交由當(dāng)前連接的 Handler 來(lái)處理
(4)Handler 會(huì)完成 read -> 業(yè)務(wù)處理 -> send 的完整業(yè)務(wù)流程
1.2、優(yōu)缺點(diǎn):
單 Reactor 單線程模型的優(yōu)點(diǎn)在于將所有處理邏輯放在一個(gè)線程中實(shí)現(xiàn),沒有多線程、進(jìn)程通信、競(jìng)爭(zhēng)的問題。但該模型在性能與可靠性方面存在比較嚴(yán)重的問題:
- ① 性能:只在代碼上進(jìn)行組件的區(qū)分,整體操作還是單線程,無(wú)法充分利用 CPU 資源,并且 Handler 業(yè)務(wù)處理部分沒有異步,一個(gè) Reactor 既要負(fù)責(zé)處理連接請(qǐng)求,又要負(fù)責(zé)處理讀寫請(qǐng)求,一般來(lái)說(shuō)處理連接請(qǐng)求是很快的,但處理讀寫請(qǐng)求時(shí)涉及到業(yè)務(wù)邏輯處理,相對(duì)慢很多。所以 Reactor 在處理讀寫請(qǐng)求時(shí),其他請(qǐng)求只能等著,容易造成系統(tǒng)的性能瓶頸
- ② 可靠性:一旦 Reactor 線程意外中斷或者進(jìn)入死循環(huán),會(huì)導(dǎo)致整個(gè)系統(tǒng)通信模塊不可用,不能接收和處理外部消息,造成節(jié)點(diǎn)故障
所以該單Reactor單進(jìn)程模型不適用于計(jì)算密集型的場(chǎng)景,只適用于業(yè)務(wù)處理非??焖俚膱?chǎng)景。Redis的線程模型就是基于單 Reactor 單線程模型實(shí)現(xiàn)的,因?yàn)?Redis 業(yè)務(wù)處理主要是在內(nèi)存中完成,操作的速度是很快的,性能瓶頸不在 CPU 上,所以 Redis 對(duì)于命令的處理是單進(jìn)程的。
2、單 Reactor 多線程模型:
為了解決單Reactor單線程模型存在的性能問題,就演進(jìn)出了單 Reactor 多線程模型,該模型在事件處理器部分采用了多線程(線程池)
2.1、處理流程:
(1)Reactor 線程通過(guò) select 監(jiān)聽事件,收到事件后通過(guò) Dispatch 進(jìn)行分發(fā)
(2)如果是連接建立事件,則將事件分發(fā)給 Acceptor,Acceptor 會(huì)通過(guò) accept() 方法獲取連接,并創(chuàng)建一個(gè)Handler 對(duì)象來(lái)處理后續(xù)的響應(yīng)事件
(3)如果是IO讀寫事件,則 Reactor 會(huì)將該事件交由當(dāng)前連接對(duì)應(yīng)的 Handler 來(lái)處理
(4)與單Reactor單線程不同的是,Handler 不再做具體業(yè)務(wù)處理,只負(fù)責(zé)接收和響應(yīng)事件,通過(guò) read 接收數(shù)據(jù)后,將數(shù)據(jù)發(fā)送給后面的 Worker 線程池進(jìn)行業(yè)務(wù)處理。
(5)Worker 線程池再分配線程進(jìn)行業(yè)務(wù)處理,完成后將響應(yīng)結(jié)果發(fā)給 Handler 進(jìn)行處理。
(6)Handler 收到響應(yīng)結(jié)果后通過(guò) send 將響應(yīng)結(jié)果返回給 Client。
2.2、優(yōu)缺點(diǎn):
相對(duì)于第一種模型來(lái)說(shuō),在處理業(yè)務(wù)邏輯,也就是獲取到 IO讀寫事件之后,交由線程池來(lái)處理,Handler 收到響應(yīng)后通過(guò) send 將響應(yīng)結(jié)果返回給客戶端。這樣可以降低 Reactor 的性能開銷,從而更專注的做事件分發(fā)工作了,提升整個(gè)應(yīng)用的吞吐,并且 Handler 使用了多線程模式,可以充分利用 CPU 的性能。但是這個(gè)模型存在的問題:
(1)Handler 使用多線程模式,自然帶來(lái)了多線程競(jìng)爭(zhēng)資源的開銷,同時(shí)涉及共享數(shù)據(jù)的互斥和保護(hù)機(jī)制,實(shí)現(xiàn)比較復(fù)雜
(2)單個(gè) Reactor 承擔(dān)所有事件的監(jiān)聽、分發(fā)和響應(yīng),對(duì)于高并發(fā)場(chǎng)景,容易造成性能瓶頸。
3、主從 Reactor 多線程模型:
單Reactor多線程模型解決了 Handler 單線程的性能問題,但是 Reactor 還是單線程的,對(duì)于高并發(fā)場(chǎng)景還是會(huì)有性能瓶頸,所以需要將 Reactor 調(diào)整為多線程模式,也就是接下來(lái)要介紹的主從 Reactor 多線程模型。主從 Reactor 多線程模型將 Reactor 分成兩部分:
(1)MainReactor:只負(fù)責(zé)處理連接建立事件,通過(guò) select 監(jiān)聽 server socket,將建立的 socketChannel 指定注冊(cè)給 subReactor,通常一個(gè)線程就可以了
(2)SubReactor:負(fù)責(zé)讀寫事件,維護(hù)自己的 selector,基于 MainReactor 注冊(cè)的 SocketChannel 進(jìn)行多路分離 IO 讀寫事件,讀寫網(wǎng)絡(luò)數(shù)據(jù),并將業(yè)務(wù)處理交由 worker 線程池來(lái)完成。SubReactor 的個(gè)數(shù)一般和 CPU 個(gè)數(shù)相同
3.1、處理流程:
(1)主線程中的 MainReactor 對(duì)象通過(guò) select 監(jiān)聽事件,接收到事件后通過(guò) Dispatch 進(jìn)行分發(fā),如果事件類型為連接建立事件則分發(fā)給 Acceptor 進(jìn)行連接建立
連接建立:
① 從主線程池中隨機(jī)選擇一個(gè) Reactor 線程作為 Acceptor 線程,用于綁定監(jiān)聽端口,接收客戶端連接
② Acceptor 線程接收客戶端連接請(qǐng)求之后創(chuàng)建新的 SocketChannel,將其注冊(cè)到主線程池的其它 Reactor 線程上,由其負(fù)責(zé)接入認(rèn)證、IP黑白名單過(guò)濾、握手等操作。
③ 步驟② 完成之后,業(yè)務(wù)層的鏈路正式建立,將 SocketChannel 從主線程池的 Reactor 線程的多路復(fù)用器上摘除,重新注冊(cè)到 SubReactor 線程池的線程上,并創(chuàng)建一個(gè) Handler 用于處理各種連接事件
(2)如果接收到的不是連接建立事件,則分發(fā)給 SubReactor,SubReactor 調(diào)用當(dāng)前連接對(duì)應(yīng)的 Handler 進(jìn)行處理
(3)Handler 通過(guò) read 讀取數(shù)據(jù)后,將數(shù)據(jù)分發(fā)給 Worker 線程池進(jìn)行業(yè)務(wù)處理,Worker 線程池則分配線程進(jìn)行業(yè)務(wù)處理,完成后將響應(yīng)結(jié)果發(fā)給 Handler
(4)Handler 收到響應(yīng)結(jié)果后通過(guò) send 將響應(yīng)結(jié)果返回給 Client
3.2、優(yōu)缺點(diǎn):
主從 Reactor 多線程模型的優(yōu)點(diǎn)在于主線程和子線程分工明確,主線程只負(fù)責(zé)接收新連接,子線程負(fù)責(zé)完成后續(xù)的業(yè)務(wù)處理,同時(shí)主線程和子線程的交互也很簡(jiǎn)單,子線程接收主線程的連接后,只管業(yè)務(wù)處理即可,無(wú)須關(guān)注主線程,可以直接在子線程將處理結(jié)果發(fā)送給客戶端。
該 Reactor 模型適用于高并發(fā)場(chǎng)景,并且 Netty 網(wǎng)絡(luò)通信框架也是采用這種實(shí)現(xiàn)
4、Reactor 優(yōu)缺點(diǎn):
(1)響應(yīng)快,不必為單個(gè)同步時(shí)間所阻塞,雖然 Reactor 本身依然是同步的;
(2)可以最大程度的避免復(fù)雜的多線程及同步問題,并且避免了多線程/進(jìn)程的切換開銷
(3)可擴(kuò)展性,可以方便地通過(guò)增加 Reactor 實(shí)例個(gè)數(shù)來(lái)充分利用 CPU 資源;
(4)可復(fù)用性,Reactor 模型本身與具體事件處理邏輯無(wú)關(guān),具有很高的復(fù)用性。
Reacotr 模型是一種非阻塞同步網(wǎng)絡(luò)模型,除此之外,還有一種 Proactor 的異步網(wǎng)絡(luò)模型,對(duì) 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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springmvc學(xué)習(xí)筆記-返回json的日期格式問題的解決方法
本篇文章主要介紹了springmvc學(xué)習(xí)筆記-返回json的日期格式問題的解決方法,解決了日期格式的輸出,有興趣的可以了解一下。2017-01-01MyBatis實(shí)現(xiàn)樂觀鎖和悲觀鎖的示例代碼
在數(shù)據(jù)庫(kù)操作中,樂觀鎖和悲觀鎖是兩種常見的并發(fā)控制策略,本文主要介紹了MyBatis實(shí)現(xiàn)樂觀鎖和悲觀鎖的示例代碼,具有一定的參考價(jià)值,感興趣的可以了解一下2024-07-07Java內(nèi)存劃分:運(yùn)行時(shí)數(shù)據(jù)區(qū)域
聽說(shuō)Java運(yùn)行時(shí)環(huán)境的內(nèi)存劃分是挺進(jìn)BAT的必經(jīng)之路,這篇文章主要給大家介紹了關(guān)于Java運(yùn)行時(shí)數(shù)據(jù)區(qū)域(內(nèi)存劃分)的相關(guān)資料,需要的朋友可以參考下2021-07-07Spring Security驗(yàn)證流程剖析及自定義驗(yàn)證方法
Spring Security是一個(gè)能夠?yàn)榛赟pring的企業(yè)應(yīng)用系統(tǒng)提供聲明式的安全訪問控制解決方案的安全框架。這篇文章主要介紹了Spring Security驗(yàn)證流程剖析及自定義驗(yàn)證方法,需要的朋友可以參考下2018-03-03Springboot+WebSocket實(shí)現(xiàn)在線聊天功能
WebSocket協(xié)議是基于TCP的一種新的網(wǎng)絡(luò)協(xié)議。這篇文章主要為大家介紹了如何利用Springboot和WebSocket實(shí)現(xiàn)在線聊天功能,感興趣的小伙伴可以了解一下2023-02-02解決IDEA上循環(huán)依賴報(bào)錯(cuò)問題Error:java: Annotation processing&n
這篇文章主要介紹了解決IDEA上循環(huán)依賴報(bào)錯(cuò)問題Error:java: Annotation processing is not supported for module cycles,具有很好的參考價(jià)值,希望對(duì)大家有所幫助2023-10-10