Reactor模式
?
Reactor 模式主要由 Reactor 和處理資源池這兩個核心部分組成,它倆負責的事情如下:
- Reactor 負責監(jiān)聽和分發(fā)事件,事件類型包含連接事件、讀寫事件;
- 處理資源池負責處理事件,如 read -> 業(yè)務邏輯 -> send;
「多 Reactor 單進程 / 線程」實現(xiàn)方案相比「單 Reactor 單進程 / 線程」方案,不僅復雜而且也沒有性能優(yōu)勢,因此實際中并沒有應用。
剩下的 3 個方案都是比較經(jīng)典的,且都有應用在實際的項目中:
單 Reactor 單進程 / 線程;
單 Reactor 多線程 / 進程;
多 Reactor 多進程 / 線程;
1.??單 Reactor 單進程 / 線程
可以看到進程里有?Reactor、Acceptor、Handler?這三個對象:
- Reactor 對象的作用是監(jiān)聽和分發(fā)事件;
- Acceptor 對象的作用是獲取連接;
- Handler 對象的作用是處理業(yè)務
但是,這種方案存在 2 個缺點:
第一個缺點,因為只有一個進程,無法充分利用 多核 CPU 的性能;
第二個缺點,Handler 對象在業(yè)務處理時,整個進程是無法處理其他連接的事件的,如果業(yè)務處理耗時比較長,那么就造成響應的延遲;
所以,單 Reactor 單進程的方案不適用計算機密集型的場景,只適用于業(yè)務處理非??焖俚膱鼍?。
?
?2.單 Reactor 多線程 / 多進程
如果要克服「單 Reactor 單線程 / 進程」方案的缺點,那么就需要引入多線程 / 多進程,這樣就產(chǎn)生了單 Reactor 多線程 / 多進程的方案。
????????Reactor 對象通過 select (IO 多路復用接口) 監(jiān)聽事件,收到事件后通過 dispatch 進行分發(fā),具體分發(fā)給 Acceptor 對象還是 Handler 對象,還要看收到的事件類型;
????????如果是連接建立的事件,則交由 Acceptor 對象進行處理,Acceptor 對象會通過 accept 方法 獲取連接,并創(chuàng)建一個 Handler 對象來處理后續(xù)的響應事件;
????????如果不是連接建立事件, 則交由當前連接對應的 Handler 對象來進行響應;
上面的三個步驟和單 Reactor 單線程方案是一樣的,接下來的步驟就開始不一樣了:
????????Handler 對象不再負責業(yè)務處理,只負責數(shù)據(jù)的接收和發(fā)送,Handler 對象通過 read 讀取到數(shù)據(jù)后,會將數(shù)據(jù)發(fā)給子線程里的 Processor 對象進行業(yè)務處理;
????????子線程里的 Processor 對象就進行業(yè)務處理,處理完后,將結果發(fā)給主線程中的 Handler 對象,接著由 Handler 通過 send 方法將響應結果發(fā)送給 client;
????????引入多線程確實可以充分利用多核 CPU 的性能,但也帶來了共享資源的競爭和同步的問題。使用互斥鎖是一種常見的手段,但需要小心避免死鎖和性能問題。
????????關于單 Reactor 多進程方案,你提到了一些問題,確實存在挑戰(zhàn)。進程間通信的復雜性和父進程需要了解子進程要發(fā)送數(shù)據(jù)給哪個客戶端的問題增加了實現(xiàn)的復雜度。相對而言,多線程之間可以共享數(shù)據(jù),雖然需要考慮并發(fā)問題,但處理起來相對簡單。
「單 Reactor」的模式在面對瞬間高并發(fā)的場景時可能成為性能瓶頸。這是因為單個 Reactor 在主線程中運行,如果事件處理耗時較長,可能導致其他事件的等待,降低系統(tǒng)的響應性能。這是一個需要權衡的問題,可以通過使用多個 Reactor 實例或者其他更復雜的事件處理模型來解決。
3.? 多 Reactor 多進程 / 線程
? ? ? ? 1 .主線程中的 MainReactor 對象通過 select 監(jiān)控連接建立事件,收到事件后通過 Acceptor 對象中的 accept 獲取連接,將新的連接分配給某個子線程;
? ? ? ? 2. 子線程中的 SubReactor 對象將 MainReactor 對象分配的連接加入 select 繼續(xù)進行監(jiān)聽,并創(chuàng)建一個 Handler 用于處理連接的響應事件。
? ? ? ? 3.如果有新的事件發(fā)生時,SubReactor 對象會調(diào)用當前連接對應的 Handler 對象來進行響應。Handler 對象通過 read -> 業(yè)務處理 -> send 的流程來完成完整的業(yè)務流程。?
多 Reactor 多線程的方案雖然看起來復雜的,但是實際實現(xiàn)時比單 Reactor 多線程的方案要簡單的多,原因如下:
????????主線程和子線程分工明確,主線程只負責接收新連接,子線程負責完成后續(xù)的業(yè)務處理。
????????主線程和子線程的交互很簡單,主線程只需要把新連接傳給子線程,子線程無須返回數(shù)據(jù),直接就可以在子線程將處理結果發(fā)送給客戶端。
-
- Nginx 是一個高性能的 HTTP 和反向代理服務器。它采用了多 Reactor 多進程的方案,其中主進程負責初始化 socket,而具體的連接處理則由子進程的 Reactor 負責。這種模型有效地避免了驚群現(xiàn)象,并且每個子進程都有自己的 Reactor 處理連接,提高了并發(fā)性。
????????對于 Nginx 中提到的差異,即主進程只負責初始化 socket,具體的連接處理由子進程的 Reactor 完成,這是一種典型的 Nginx 的優(yōu)化策略,避免了主進程直接參與連接的處理,降低了主進程的壓力。這樣的設計有效地避免了驚群現(xiàn)象,提高了整體性能。這種模型也被稱為 "master-worker" 模型。
Proactor模式
?????????可惜的是,在 Linux 下的異步 I/O 是不完善的, aio 系列函數(shù)是由 POSIX 定義的異步操作接口,不是真正的操作系統(tǒng)級別支持的,而是在用戶空間模擬出來的異步,并且僅僅支持基于本地文件的 aio 異步操作,網(wǎng)絡編程中的 socket 是不支持的,這也使得基于 Linux 的高性能網(wǎng)絡程序都是使用 Reactor 方案。
區(qū)別:
????????Reactor 是非阻塞同步網(wǎng)絡模式,感知的是就緒可讀寫事件。在每次感知到有事件發(fā)生(比如可讀就緒事件)后,就需要應用進程主動調(diào)用 read 方法來完成數(shù)據(jù)的讀取,也就是要應用進程主動將 socket 接收緩存中的數(shù)據(jù)讀到應用進程內(nèi)存中,這個過程是同步的,讀取完數(shù)據(jù)后應用進程才能處理數(shù)據(jù)。
????????Proactor 是異步網(wǎng)絡模式, 感知的是已完成的讀寫事件。在發(fā)起異步讀寫請求時,需要傳入數(shù)據(jù)緩沖區(qū)的地址(用來存放結果數(shù)據(jù))等信息,這樣系統(tǒng)內(nèi)核才可以自動幫我們把數(shù)據(jù)的讀寫工作完成,這里的讀寫工作全程由操作系統(tǒng)來做,并不需要像 Reactor 那樣還需要應用進程主動發(fā)起 read/write 來讀寫數(shù)據(jù),操作系統(tǒng)完成讀寫工作后,就會通知應用進程直接處理數(shù)據(jù)。
因此,Reactor 可以理解為「來了事件操作系統(tǒng)通知應用進程,讓應用進程來處理」,而 Proactor 可以理解為「來了事件操作系統(tǒng)來處理,處理完再通知應用進程」。這里的「事件」就是有新連接、有數(shù)據(jù)可讀、有數(shù)據(jù)可寫的這些 I/O 事件這里的「處理」包含從驅(qū)動讀取到內(nèi)核以及從內(nèi)核讀取到用戶空間。文章來源:http://www.zghlxwxcb.cn/news/detail-802487.html
模擬proactor?
文章來源地址http://www.zghlxwxcb.cn/news/detail-802487.html
到了這里,關于Reactor和Proactor兩種高效的事件處理模式的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!