五分钟快速理解 Reactor 模型
點(diǎn)擊上方“朱小廝的博客”,選擇“設(shè)為星標(biāo)”
后臺(tái)回復(fù)"書",獲取
后臺(tái)回復(fù)“k8s”,可領(lǐng)取k8s資料
本文將介紹基于進(jìn)程/線程模型,服務(wù)器如何處理請(qǐng)求。值得說(shuō)明的是,具體選擇線程還是進(jìn)程,更多是與平臺(tái)及編程語(yǔ)言相關(guān)。
例如 C 語(yǔ)言使用線程和進(jìn)程都可以(例如 Nginx 使用進(jìn)程,Memcached 使用線程),Java 語(yǔ)言一般使用線程(例如 Netty),為了描述方便,下面都使用線程來(lái)進(jìn)行描述。
1、線程模型1:傳統(tǒng)阻塞 I/O 服務(wù)模型
?
特點(diǎn):
1)采用阻塞式 I/O 模型獲取輸入數(shù)據(jù);
2)每個(gè)連接都需要獨(dú)立的線程完成數(shù)據(jù)輸入,業(yè)務(wù)處理,數(shù)據(jù)返回的完整操作。
存在問(wèn)題:
1)當(dāng)并發(fā)數(shù)較大時(shí),需要?jiǎng)?chuàng)建大量線程來(lái)處理連接,系統(tǒng)資源占用較大;
2)連接建立后,如果當(dāng)前線程暫時(shí)沒(méi)有數(shù)據(jù)可讀,則線程就阻塞在 Read 操作上,造成線程資源浪費(fèi)。
2、線程模型2:Reactor 模式
2.1基本介紹
針對(duì)傳統(tǒng)阻塞 I/O 服務(wù)模型的 2 個(gè)缺點(diǎn),比較常見(jiàn)的有如下解決方案:?
1)基于 I/O 復(fù)用模型:多個(gè)連接共用一個(gè)阻塞對(duì)象,應(yīng)用程序只需要在一個(gè)阻塞對(duì)象上等待,無(wú)需阻塞等待所有連接。當(dāng)某條連接有新的數(shù)據(jù)可以處理時(shí),操作系統(tǒng)通知應(yīng)用程序,線程從阻塞狀態(tài)返回,開始進(jìn)行業(yè)務(wù)處理;
2)基于線程池復(fù)用線程資源:不必再為每個(gè)連接創(chuàng)建線程,將連接完成后的業(yè)務(wù)處理任務(wù)分配給線程進(jìn)行處理,一個(gè)線程可以處理多個(gè)連接的業(yè)務(wù)。
I/O 復(fù)用結(jié)合線程池,這就是 Reactor 模式基本設(shè)計(jì)思想,如下圖:
?
Reactor 模式,是指通過(guò)一個(gè)或多個(gè)輸入同時(shí)傳遞給服務(wù)處理器的服務(wù)請(qǐng)求的事件驅(qū)動(dòng)處理模式。?
服務(wù)端程序處理傳入多路請(qǐng)求,并將它們同步分派給請(qǐng)求對(duì)應(yīng)的處理線程,Reactor 模式也叫 Dispatcher 模式。
即 I/O 多了復(fù)用統(tǒng)一監(jiān)聽(tīng)事件,收到事件后分發(fā)(Dispatch 給某進(jìn)程),是編寫高性能網(wǎng)絡(luò)服務(wù)器的必備技術(shù)之一。
Reactor 模式中有 2 個(gè)關(guān)鍵組成:
1)Reactor:Reactor 在一個(gè)單獨(dú)的線程中運(yùn)行,負(fù)責(zé)監(jiān)聽(tīng)和分發(fā)事件,分發(fā)給適當(dāng)?shù)奶幚沓绦騺?lái)對(duì) IO 事件做出反應(yīng)。它就像公司的電話接線員,它接聽(tīng)來(lái)自客戶的電話并將線路轉(zhuǎn)移到適當(dāng)?shù)穆?lián)系人;
2)Handlers:處理程序執(zhí)行 I/O 事件要完成的實(shí)際事件,類似于客戶想要與之交談的公司中的實(shí)際官員。Reactor 通過(guò)調(diào)度適當(dāng)?shù)奶幚沓绦騺?lái)響應(yīng) I/O 事件,處理程序執(zhí)行非阻塞操作。
根據(jù) Reactor 的數(shù)量和處理資源池線程的數(shù)量不同,有 3 種典型的實(shí)現(xiàn):
1)單 Reactor 單線程;
2)單 Reactor 多線程;
3)主從 Reactor 多線程。
下面詳細(xì)介紹這 3 種實(shí)現(xiàn)方式。
2.2單 Reactor 單線程
其中,Select 是前面 I/O 復(fù)用模型介紹的標(biāo)準(zhǔn)網(wǎng)絡(luò)編程 API,可以實(shí)現(xiàn)應(yīng)用程序通過(guò)一個(gè)阻塞對(duì)象監(jiān)聽(tīng)多路連接請(qǐng)求,其他方案示意圖類似。
方案說(shuō)明:
1)Reactor 對(duì)象通過(guò) Select 監(jiān)控客戶端請(qǐng)求事件,收到事件后通過(guò) Dispatch 進(jìn)行分發(fā);
2)如果是建立連接請(qǐng)求事件,則由 Acceptor 通過(guò) Accept 處理連接請(qǐng)求,然后創(chuàng)建一個(gè) Handler 對(duì)象處理連接完成后的后續(xù)業(yè)務(wù)處理;
3)如果不是建立連接事件,則 Reactor 會(huì)分發(fā)調(diào)用連接對(duì)應(yīng)的 Handler 來(lái)響應(yīng);
4)Handler 會(huì)完成 Read→業(yè)務(wù)處理→Send 的完整業(yè)務(wù)流程。
優(yōu)點(diǎn):模型簡(jiǎn)單,沒(méi)有多線程、進(jìn)程通信、競(jìng)爭(zhēng)的問(wèn)題,全部都在一個(gè)線程中完成。
缺點(diǎn):性能問(wèn)題,只有一個(gè)線程,無(wú)法完全發(fā)揮多核 CPU 的性能。Handler 在處理某個(gè)連接上的業(yè)務(wù)時(shí),整個(gè)進(jìn)程無(wú)法處理其他連接事件,很容易導(dǎo)致性能瓶頸。
可靠性問(wèn)題,線程意外跑飛,或者進(jìn)入死循環(huán),會(huì)導(dǎo)致整個(gè)系統(tǒng)通信模塊不可用,不能接收和處理外部消息,造成節(jié)點(diǎn)故障。
使用場(chǎng)景:客戶端的數(shù)量有限,業(yè)務(wù)處理非常快速,比如 Redis,業(yè)務(wù)處理的時(shí)間復(fù)雜度 O(1)。
2.3單 Reactor 多線程
方案說(shuō)明:
1)Reactor 對(duì)象通過(guò) Select 監(jiān)控客戶端請(qǐng)求事件,收到事件后通過(guò) Dispatch 進(jìn)行分發(fā);
2)如果是建立連接請(qǐng)求事件,則由 Acceptor 通過(guò) Accept 處理連接請(qǐng)求,然后創(chuàng)建一個(gè) Handler 對(duì)象處理連接完成后續(xù)的各種事件;
3)如果不是建立連接事件,則 Reactor 會(huì)分發(fā)調(diào)用連接對(duì)應(yīng)的 Handler 來(lái)響應(yīng);
4)Handler 只負(fù)責(zé)響應(yīng)事件,不做具體業(yè)務(wù)處理,通過(guò) Read 讀取數(shù)據(jù)后,會(huì)分發(fā)給后面的 Worker 線程池進(jìn)行業(yè)務(wù)處理;
5)Worker 線程池會(huì)分配獨(dú)立的線程完成真正的業(yè)務(wù)處理,如何將響應(yīng)結(jié)果發(fā)給 Handler 進(jìn)行處理;
6)Handler 收到響應(yīng)結(jié)果后通過(guò) Send 將響應(yīng)結(jié)果返回給 Client。
優(yōu)點(diǎn):可以充分利用多核 CPU 的處理能力。
缺點(diǎn):多線程數(shù)據(jù)共享和訪問(wèn)比較復(fù)雜;Reactor 承擔(dān)所有事件的監(jiān)聽(tīng)和響應(yīng),在單線程中運(yùn)行,高并發(fā)場(chǎng)景下容易成為性能瓶頸。
2.4主從 Reactor 多線程
針對(duì)單 Reactor 多線程模型中,Reactor 在單線程中運(yùn)行,高并發(fā)場(chǎng)景下容易成為性能瓶頸,可以讓 Reactor 在多線程中運(yùn)行。
方案說(shuō)明:
1)Reactor 主線程 MainReactor 對(duì)象通過(guò) Select 監(jiān)控建立連接事件,收到事件后通過(guò) Acceptor 接收,處理建立連接事件;
2)Acceptor 處理建立連接事件后,MainReactor 將連接分配 Reactor 子線程給 SubReactor 進(jìn)行處理;
3)SubReactor 將連接加入連接隊(duì)列進(jìn)行監(jiān)聽(tīng),并創(chuàng)建一個(gè) Handler 用于處理各種連接事件;
4)當(dāng)有新的事件發(fā)生時(shí),SubReactor 會(huì)調(diào)用連接對(duì)應(yīng)的 Handler 進(jìn)行響應(yīng);
5)Handler 通過(guò) Read 讀取數(shù)據(jù)后,會(huì)分發(fā)給后面的 Worker 線程池進(jìn)行業(yè)務(wù)處理;
6)Worker 線程池會(huì)分配獨(dú)立的線程完成真正的業(yè)務(wù)處理,如何將響應(yīng)結(jié)果發(fā)給 Handler 進(jìn)行處理;
7)Handler 收到響應(yīng)結(jié)果后通過(guò) Send 將響應(yīng)結(jié)果返回給 Client。
優(yōu)點(diǎn):父線程與子線程的數(shù)據(jù)交互簡(jiǎn)單職責(zé)明確,父線程只需要接收新連接,子線程完成后續(xù)的業(yè)務(wù)處理。
父線程與子線程的數(shù)據(jù)交互簡(jiǎn)單,Reactor 主線程只需要把新連接傳給子線程,子線程無(wú)需返回?cái)?shù)據(jù)。
這種模型在許多項(xiàng)目中廣泛使用,包括 Nginx 主從 Reactor 多進(jìn)程模型,Memcached 主從多線程,Netty 主從多線程模型的支持。
2.5小結(jié)
3 種模式可以用個(gè)比喻來(lái)理解:(餐廳常常雇傭接待員負(fù)責(zé)迎接顧客,當(dāng)顧客入坐后,侍應(yīng)生專門為這張桌子服務(wù))
1)單 Reactor 單線程,接待員和侍應(yīng)生是同一個(gè)人,全程為顧客服務(wù);
2)單 Reactor 多線程,1 個(gè)接待員,多個(gè)侍應(yīng)生,接待員只負(fù)責(zé)接待;
3)主從 Reactor 多線程,多個(gè)接待員,多個(gè)侍應(yīng)生。
Reactor 模式具有如下的優(yōu)點(diǎn):
1)響應(yīng)快,不必為單個(gè)同步時(shí)間所阻塞,雖然 Reactor 本身依然是同步的;
2)編程相對(duì)簡(jiǎn)單,可以最大程度的避免復(fù)雜的多線程及同步問(wèn)題,并且避免了多線程/進(jìn)程的切換開銷;
3)可擴(kuò)展性,可以方便的通過(guò)增加 Reactor 實(shí)例個(gè)數(shù)來(lái)充分利用 CPU 資源;
4)可復(fù)用性,Reactor 模型本身與具體事件處理邏輯無(wú)關(guān),具有很高的復(fù)用性。
3、線程模型2:Proactor 模型
在 Reactor 模式中,Reactor 等待某個(gè)事件或者可應(yīng)用或者操作的狀態(tài)發(fā)生(比如文件描述符可讀寫,或者是 Socket 可讀寫)。
然后把這個(gè)事件傳給事先注冊(cè)的 Handler(事件處理函數(shù)或者回調(diào)函數(shù)),由后者來(lái)做實(shí)際的讀寫操作。
其中的讀寫操作都需要應(yīng)用程序同步操作,所以 Reactor 是非阻塞同步網(wǎng)絡(luò)模型。
如果把 I/O 操作改為異步,即交給操作系統(tǒng)來(lái)完成就能進(jìn)一步提升性能,這就是異步網(wǎng)絡(luò)模型 Proactor。
?
Proactor 是和異步 I/O 相關(guān)的,詳細(xì)方案如下:
1)Proactor Initiator 創(chuàng)建 Proactor 和 Handler 對(duì)象,并將 Proactor 和 Handler 都通過(guò) AsyOptProcessor(Asynchronous Operation Processor)注冊(cè)到內(nèi)核;
2)AsyOptProcessor 處理注冊(cè)請(qǐng)求,并處理 I/O 操作;
3)AsyOptProcessor 完成 I/O 操作后通知 Proactor;
4)Proactor 根據(jù)不同的事件類型回調(diào)不同的 Handler 進(jìn)行業(yè)務(wù)處理;
5)Handler 完成業(yè)務(wù)處理。
可以看出 Proactor 和 Reactor 的區(qū)別:
1)Reactor 是在事件發(fā)生時(shí)就通知事先注冊(cè)的事件(讀寫在應(yīng)用程序線程中處理完成);
2)Proactor 是在事件發(fā)生時(shí)基于異步 I/O 完成讀寫操作(由內(nèi)核完成),待 I/O 操作完成后才回調(diào)應(yīng)用程序的處理器來(lái)進(jìn)行業(yè)務(wù)處理。
理論上 Proactor 比 Reactor 效率更高,異步 I/O 更加充分發(fā)揮 DMA(Direct Memory Access,直接內(nèi)存存取)的優(yōu)勢(shì)。
但是Proactor有如下缺點(diǎn):?
1)編程復(fù)雜性,由于異步操作流程的事件的初始化和事件完成在時(shí)間和空間上都是相互分離的,因此開發(fā)異步應(yīng)用程序更加復(fù)雜。應(yīng)用程序還可能因?yàn)榉聪虻牧骺囟兊酶与y以 Debug;
2)內(nèi)存使用,緩沖區(qū)在讀或?qū)懖僮鞯臅r(shí)間段內(nèi)必須保持住,可能造成持續(xù)的不確定性,并且每個(gè)并發(fā)操作都要求有獨(dú)立的緩存,相比 Reactor 模式,在 Socket 已經(jīng)準(zhǔn)備好讀或?qū)懬?#xff0c;是不要求開辟緩存的;
3)操作系統(tǒng)支持,Windows 下通過(guò) IOCP 實(shí)現(xiàn)了真正的異步 I/O,而在 Linux 系統(tǒng)下,Linux 2.6 才引入,目前異步 I/O 還不完善。
因此在 Linux下實(shí)現(xiàn)高并發(fā)網(wǎng)絡(luò)編程都是以 Reactor 模型為主。
想知道更多?掃描下面的二維碼關(guān)注我
后臺(tái)回復(fù)"技術(shù)",加入技術(shù)群
后臺(tái)回復(fù)“k8s”,可領(lǐng)取k8s資料
【精彩推薦】
ClickHouse到底是什么?為什么如此牛逼!
原來(lái)ElasticSearch還可以這么理解
面試官:InnoDB中一棵B+樹可以存放多少行數(shù)據(jù)?
架構(gòu)之道:分離業(yè)務(wù)邏輯和技術(shù)細(xì)節(jié)
星巴克不使用兩階段提交
面試官:Redis新版本開始引入多線程,談?wù)勀愕目捶?#xff1f;
喜馬拉雅自研網(wǎng)關(guān)架構(gòu)演進(jìn)過(guò)程
收藏:存儲(chǔ)知識(shí)全面總結(jié)
微博千萬(wàn)級(jí)規(guī)模高性能高并發(fā)的網(wǎng)絡(luò)架構(gòu)設(shè)計(jì)
總結(jié)
以上是生活随笔為你收集整理的五分钟快速理解 Reactor 模型的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 干货:如何正确描述存储IO类型?
- 下一篇: DDD 领域驱动设计:贫血模型、充血模型