新聞中心
前文送分來(lái)了,華為一面,介紹下五種 IO 模型 我們解釋過(guò),操作系統(tǒng)系統(tǒng)如何獲取輸入和輸出的數(shù)據(jù),就是 I/O 模型干的事。

站在用戶的角度思考問(wèn)題,與客戶深入溝通,找到忻城網(wǎng)站設(shè)計(jì)與忻城網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:成都網(wǎng)站建設(shè)、網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、國(guó)際域名空間、網(wǎng)絡(luò)空間、企業(yè)郵箱。業(yè)務(wù)覆蓋忻城地區(qū)。
那怎么通過(guò)輸入數(shù)據(jù)得到的輸出數(shù)據(jù)的?換句話說(shuō),操作系統(tǒng)是怎么處理輸入數(shù)據(jù)的(怎么處理請(qǐng)求的)?這就是線程模型(或者說(shuō)進(jìn)程模型)的事了。
所以,當(dāng)我們想要設(shè)計(jì)一個(gè)服務(wù)端的并發(fā)模型時(shí),主要有如下兩個(gè)關(guān)鍵點(diǎn):
- 服務(wù)器如何管理連接,獲取輸入輸出數(shù)據(jù):基于 「I/O 模型」管理連接
- 服務(wù)器如何處理請(qǐng)求:基于「線程/進(jìn)程模型」處理請(qǐng)求
值得說(shuō)明的是,具體選擇線程還是進(jìn)程來(lái)處理請(qǐng)求,更多是與平臺(tái)及編程語(yǔ)言相關(guān),例如 Nginx 使用進(jìn)程,Memcached 使用線程,而 C 語(yǔ)言使用線程和進(jìn)程都可以,Java 語(yǔ)言一般使用線程(例如 Netty),為方便行文,下文統(tǒng)一用線程模型
主要有三類線程模型:
- 阻塞 I/O 模型(Blocking I/O Model)
- Reactor 模型
單 Reactor 單線程
單 Reactor 多線程
主從 Reactor 多線程
- Proactor 模型
Blocking I/O Model
方案說(shuō)明
阻塞 I/O 模型也被稱為同步 I/O 模型。在這種模型下,為每一個(gè)請(qǐng)求分配一個(gè)線程,并且當(dāng)一個(gè)線程執(zhí)行一個(gè) I/O 操作時(shí),它會(huì)一直等待,直到 I/O 操作完成并返回結(jié)果。在這個(gè)過(guò)程中,該線程會(huì)被阻塞,無(wú)法執(zhí)行其他任務(wù)。
廢話不多說(shuō),看圖就明白了:
阻塞 I/O 模型原理示例圖
特點(diǎn)
- 采用「阻塞 I/O 模型」獲取輸入數(shù)據(jù),實(shí)現(xiàn)簡(jiǎn)單
- 每個(gè)連接都需要獨(dú)立的線程完成數(shù)據(jù)輸入、業(yè)務(wù)處理、數(shù)據(jù)返回的完整操作
缺點(diǎn)
- 當(dāng)并發(fā)數(shù)較大時(shí),需要?jiǎng)?chuàng)建大量線程來(lái)處理連接,系統(tǒng)資源占用較大
- 連接建立后,如果當(dāng)前線程暫時(shí)沒(méi)有數(shù)據(jù)可讀,則線程就阻塞在 read 操作上,造成線程資源浪費(fèi),CPU 的利用率非常低,因?yàn)榫€程會(huì)被阻塞很長(zhǎng)時(shí)間,等待 I/O 操作的完成,如果線程阻塞過(guò)多甚至?xí)?dǎo)致系統(tǒng)崩潰
Reactor Model
針對(duì)傳統(tǒng)阻塞 I/O 模式的 2 個(gè)缺點(diǎn),比較常見的有如下解決方案:
- 基于「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ù)處理
- 基于「線程池」復(fù)用線程資源,不必再為每個(gè)連接創(chuàng)建線程,將連接完成后的具體處理任務(wù)分配給線程進(jìn)行處理,一個(gè)線程可以處理多個(gè)連接的業(yè)務(wù)
I/O 多路復(fù)用 + 線程池,這就是 Reactor 模式基本設(shè)計(jì)思想
Reactor 模式中主要有 2 個(gè)關(guān)鍵組成:
- Reactor:負(fù)責(zé)監(jiān)聽和分發(fā)事件,分發(fā)給適當(dāng)?shù)?Handler 來(lái)對(duì) IO 事件做出反應(yīng)
- Handler:處理程序執(zhí)行 I/O 事件要完成的實(shí)際任務(wù)
根據(jù)Reactor的數(shù)量和處理資源池線程的數(shù)量不同,有 3 種典型的實(shí)現(xiàn):
- 單 Reactor 單線程:Single Reactor Single Thread (SRST) pattern
- 單 Reactor 多線程:Single Reactor Multi-Thread (SRMT) pattern
- 主從 Reactor 多線程:Master-Slave Reactor Multi-Thread (MSRMT) pattern
單 Reactor 單線程
方案說(shuō)明
- 一個(gè)進(jìn)程里有 Reactor、Acceptor、Handler 這三個(gè)對(duì)象:
- Reactor 對(duì)象的作用是監(jiān)聽和分發(fā)事件;
- Acceptor 對(duì)象的作用是獲取連接;
- Handler 對(duì)象的作用是處理業(yè)務(wù);
- Reactor 對(duì)象通過(guò) select 監(jiān)控客戶端請(qǐng)求事件,收到事件后通過(guò) dispatch 進(jìn)行分發(fā),具體分發(fā)給 Acceptor 對(duì)象還是 Handler 對(duì)象,還要看收到的事件類型;
- 如果是建立連接請(qǐng)求事件,則由Acceptor通過(guò)accept處理連接請(qǐng)求,然后創(chuàng)建一個(gè)Handler對(duì)象處理連接完成后的后續(xù)業(yè)務(wù)處理
- 如果不是建立連接事件,則 Reactor 會(huì)分發(fā)調(diào)用連接對(duì)應(yīng)的Handler對(duì)象來(lái)響應(yīng)
- Handler 對(duì)象通過(guò) read -> 業(yè)務(wù)處理 -> send 的流程來(lái)完成完整的業(yè)務(wù)流程
單 Reactor 單線程原理示例圖
特點(diǎn)
模型簡(jiǎn)單,所有的 I/O 操作都在一個(gè)線程中被執(zhí)行,Reactor 監(jiān)聽所有的 IO 事件,當(dāng)有事件發(fā)生時(shí),Reactor 會(huì)調(diào)用對(duì)應(yīng)的 Handler 來(lái)處理事件。沒(méi)有多線程(全部工作都在同一個(gè)線程內(nèi)完成)、線程/進(jìn)程通信、競(jìng)爭(zhēng)的問(wèn)題
缺點(diǎn)
- 性能問(wèn)題:只有一個(gè)線程,無(wú)法完全發(fā)揮多核 CPU 的性能。Handler 對(duì)象在處理某個(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)景
適用于并發(fā)需求不高、處理邏輯簡(jiǎn)單的情況,比如處理時(shí)間不敏感的任務(wù)
示例代碼
下面是一個(gè)簡(jiǎn)單的示例代碼,實(shí)現(xiàn)了單 Reactor 單線程模型
單 Reactor 多線程
方案說(shuō)明
和單 Reactor 單線程模型的區(qū)別就在于,
- Handler 只負(fù)責(zé)響應(yīng)事件,不做具體業(yè)務(wù)處理,通過(guò) read 讀取數(shù)據(jù)后,會(huì)分發(fā)給后面的 Worker 線程池進(jìn)行業(yè)務(wù)處理
- Worker 線程池會(huì)分配獨(dú)立的線程完成真正的業(yè)務(wù)處理,然后將響應(yīng)結(jié)果發(fā)給 Handler 進(jìn)行處理
- Handler 收到響應(yīng)結(jié)果后返回給 client
單 Reactor 多線程原理示例圖
特點(diǎn)
單 Reactor 多線程模型中,Reactor 仍然只有一個(gè),但是有多個(gè)線程用于處理 I/O 事件。當(dāng)有事件發(fā)生時(shí),Reactor 會(huì)將事件分發(fā)給空閑的線程處理,可以利用多核 CPU 實(shí)現(xiàn)更好的并發(fā)性能。
缺點(diǎn)
- 多線程數(shù)據(jù)共享和訪問(wèn)比較復(fù)雜
- Reactor 承擔(dān)所有事件的監(jiān)聽和響應(yīng),在單線程中運(yùn)行,高并發(fā)場(chǎng)景下容易成為性能瓶頸
適用場(chǎng)景
單 Reactor 多線程模型適用于并發(fā)需求較高、但是任務(wù)處理邏輯簡(jiǎn)單的情況,可以利用多核 CPU 提高并發(fā)性能
示例代碼
在下述代碼中,Reactor 類代表 Reactor 線程,它通過(guò) Selector(Java NIO 包中的類) 監(jiān)聽和分發(fā)事件。Acceptor 類代表事件處理器,它處理連接請(qǐng)求事件,并創(chuàng)建新的 Handler 對(duì)象處理讀寫事件。Handler 類代表具體的業(yè)務(wù)邏輯處理器,它處理讀寫事件并返回響應(yīng)。
在 Reactor 類的 run 方法中,通過(guò)調(diào)用 Selector 的 select 方法等待事件的發(fā)生,并獲取已經(jīng)就緒的事件。然后遍歷事件集合,將每個(gè)事件分發(fā)給一個(gè) Worker 線程處理。Worker 線程采用線程池來(lái)實(shí)現(xiàn)。
主從 Reactor 多線程
方案說(shuō)明
針對(duì)單 Reactor 多線程模型中,Reactor 在單線程中運(yùn)行,高并發(fā)場(chǎng)景下容易成為性能瓶頸,主從 Reactor 模型可以讓 Reactor 在多線程中運(yùn)行:
- Reactor 主線程(MainReactor)通過(guò) select 監(jiān)控建立連接事件,收到事件后通過(guò) Acceptor 接收,處理建立連接事件
- Accepto 處理建立連接事件后,MainReactor 將連接分配給 Reactor 子線程(SubReactor)進(jìn)行處理
- SubReactor 將連接加入 select 進(jìn)行監(jiān)聽,并創(chuàng)建一個(gè) Handler 用于處理該連接的響應(yīng)事件
- 當(dāng)有新的事件發(fā)生時(shí),SubReactor 會(huì)調(diào)用連接對(duì)應(yīng)的 Handler 進(jìn)行響應(yīng)
- Handler 通過(guò) read 讀取數(shù)據(jù)后,會(huì)分發(fā)給后面的 Worker 線程池進(jìn)行業(yè)務(wù)處理
- Worker 線程池會(huì)分配獨(dú)立的線程完成真正的業(yè)務(wù)處理,如何將響應(yīng)結(jié)果發(fā)給 Handler 進(jìn)行處理
- Handler 收到響應(yīng)結(jié)果后通過(guò) send 將響應(yīng)結(jié)果返回給 Client
主從 Reactor 多線程原理示意圖
特點(diǎn)
- Reactor 父線程與子線程的數(shù)據(jù)交互簡(jiǎn)單職責(zé)明確,Reactor 父線程只需要接收新連接,Reactor 子線程完成后續(xù)的業(yè)務(wù)處理
- 父線程與子線程的數(shù)據(jù)交互簡(jiǎn)單,Reactor 主線程只需要把新連接傳給子線程,子線程無(wú)需返回?cái)?shù)據(jù)
缺點(diǎn)
- 實(shí)現(xiàn)較為復(fù)雜:主從 Reactor 模式需要同時(shí)處理多個(gè) Reactor 線程之間的協(xié)作和同步,實(shí)現(xiàn)起來(lái)比較復(fù)雜,需要處理更多的細(xì)節(jié)和邊界情況。
- 可能存在的性能瓶頸:在主從 Reactor 模式中,主 Reactor 線程和從 Reactor 線程之間需要通過(guò)隊(duì)列等方式傳遞事件,可能會(huì)存在性能瓶頸。
- 調(diào)試和維護(hù)困難:主從 Reactor 模式中包含多個(gè)線程和多個(gè) Reactor 對(duì)象,調(diào)試和維護(hù)都比較困難,需要付出更多的時(shí)間和精力。
適用場(chǎng)景
主從 Reactor 多線程模型適用于并發(fā)需求非常高,任務(wù)處理邏輯復(fù)雜,可以通過(guò)多個(gè)從 Reactor 和線程池來(lái)提高并發(fā)性能。
示例代碼
在下述示例代碼中,主 Reactor 線程負(fù)責(zé)監(jiān)聽連接事件(SelectionKey.OP_ACCEPT?),從 Reactor 線程負(fù)責(zé)監(jiān)聽讀事件(SelectionKey.OP_READ?)和寫事件(SelectionKey.OP_WRITE)。當(dāng)有新連接到達(dá)時(shí),主 Reactor 線程會(huì)創(chuàng)建一個(gè)新的 Handler 對(duì)象,并將其注冊(cè)到從 Reactor 線程的 Selector 上。在 Handler 中,讀和寫事件會(huì)被提交給線程池
Proactor Model
由于 Reactor 是基于 I/O 多路復(fù)用的,所以 Reactor 其實(shí)還是同步的。
而 Proactor 是「異步」的,把 I/O 操作改為異步來(lái)進(jìn)一步提升性能
在 Proactor 線程模型中,應(yīng)用程序通過(guò)調(diào)用異步 I/O 操作向系統(tǒng)內(nèi)核發(fā)起 I/O 請(qǐng)求,內(nèi)核將請(qǐng)求放入 I/O 隊(duì)列中,并向應(yīng)用程序返回一個(gè) I/O 請(qǐng)求標(biāo)識(shí)符。應(yīng)用程序可以繼續(xù)執(zhí)行其他任務(wù),不必等待 I/O 操作完成。當(dāng)內(nèi)核完成 I/O 操作時(shí),會(huì)向應(yīng)用程序發(fā)送一個(gè)通知,并將操作結(jié)果放入 I/O 完成隊(duì)列中。應(yīng)用程序可以通過(guò)輪詢或者回調(diào)方式獲取完成隊(duì)列中的操作結(jié)果,并進(jìn)行后續(xù)的處理。
Reactor 和 Proactor 的區(qū)別:
- Reactor 是同步網(wǎng)絡(luò)模式,感知的是【就緒可讀寫事件】。在每次感知到有事件發(fā)生(比如可讀就緒事件)后,就需要應(yīng)用進(jìn)程主動(dòng)調(diào)用 read 方法來(lái)完成數(shù)據(jù)的讀取,也就是要應(yīng)用進(jìn)程主動(dòng)將 socket 接收緩存中的數(shù)據(jù)讀到應(yīng)用進(jìn)程內(nèi)存中,這個(gè)過(guò)程是同步的,讀取完數(shù)據(jù)后應(yīng)用進(jìn)程才能處理數(shù)據(jù)。
- Proactor 是異步網(wǎng)絡(luò)模式, 感知的是【已完成的讀寫事件】。在發(fā)起異步讀寫請(qǐng)求時(shí),需要傳入數(shù)據(jù)緩沖區(qū)的地址(用來(lái)存放結(jié)果數(shù)據(jù))等信息,這樣系統(tǒng)內(nèi)核才可以自動(dòng)幫我們把數(shù)據(jù)的讀寫工作完成,這里的讀寫工作全程由操作系統(tǒng)來(lái)做,并不需要像 Reactor 那樣還需要應(yīng)用進(jìn)程主動(dòng)發(fā)起 read/write 來(lái)讀寫數(shù)據(jù),內(nèi)核完成讀寫工作后,就會(huì)通知應(yīng)用進(jìn)程直接處理數(shù)據(jù)。
因此,Reactor 可以理解為「來(lái)了事件操作系統(tǒng)通知應(yīng)用進(jìn)程,讓應(yīng)用進(jìn)程來(lái)處理」,而 Proactor 可以理解為「來(lái)了事件操作系統(tǒng)來(lái)處理,處理完再通知應(yīng)用進(jìn)程」
理論上 Proactor 比 Reactor 效率更高,但是有如下缺點(diǎn):
- 編程復(fù)雜性:由于異步操作流程的事件的初始化和事件完成在時(shí)間和空間上都是相互分離的,因此開發(fā)異步應(yīng)用程序更加復(fù)雜。應(yīng)用程序還可能因?yàn)榉聪虻牧骺囟兊酶与y以Debug
- 內(nèi)存使用:緩沖區(qū)在讀或?qū)懖僮鞯臅r(shí)間段內(nèi)必須保持住,可能造成持續(xù)的不確定性,并且每個(gè)并發(fā)操作都要求有獨(dú)立的緩存,相比 Reactor 模式,在 Socket 已經(jīng)準(zhǔn)備好讀或?qū)懬?,是不要求開辟緩存的
- 操作系統(tǒng)支持:Windows 下通過(guò) IOCP 實(shí)現(xiàn)了真正的異步 I/O,而在 Linux 系統(tǒng)下,Linux2.6 才引入,目前異步 I/O 還不完善
新聞標(biāo)題:B站二面被掛,線程模型還不夠熟練?
網(wǎng)頁(yè)URL:http://www.5511xx.com/article/copodoi.html


咨詢
建站咨詢
