01.前言 · 淺析IO模型
02.圖解BIO之傳統(tǒng)編程模型
03.基于I/O復(fù)用模型的Reactor模式
04.Reactor模式的多樣變化
05.最后總結(jié)
01.前言 · 淺析IO模型
IO操作,包括網(wǎng)絡(luò)IO,文件IO。本文討論的是網(wǎng)絡(luò)編程中的概念。IO模型的話(huà),很多大佬都做過(guò)深入分析,一般分為5種模型。我們這邊也不去從LInux系統(tǒng)底層去深度剖析,從下面的IO模型對(duì)比圖,可以理解區(qū)別一下 同步阻塞 這四字中同步和阻塞各自的含義。

如圖中所示,其實(shí)我們操作IO一般就是分為倆個(gè)過(guò)程,等待數(shù)據(jù)就緒,拷貝數(shù)據(jù)從內(nèi)核復(fù)制到用戶(hù)空間。前四種模型都是阻塞的,并且第二階段都是阻塞的。我們可以從下面的概念來(lái)理解一下同步、阻塞的概念。
(1).阻塞調(diào)用與非阻塞調(diào)用
阻塞調(diào)用,調(diào)用結(jié)果返回之前,當(dāng)前線程會(huì)被掛起,一直等待,直到結(jié)果返回;
非阻塞調(diào)用則不用等調(diào)用結(jié)果返回,不會(huì)阻塞當(dāng)前線程,可以去干其他事情。
(2).同步處理和異步處理
同步處理是指,被調(diào)用方得到最終結(jié)果之后返回給調(diào)用方;
異步處理是指,被調(diào)用方不等處理結(jié)束先返回響應(yīng),再進(jìn)行計(jì)算,當(dāng)計(jì)算完成時(shí),通知調(diào)用方并將結(jié)果返回給調(diào)用方。
02.圖解BIO之傳統(tǒng)編程模型
BIO BLockingIO(同步阻塞模式)。當(dāng)我們調(diào)用類(lèi)似accept(),read(),connect()等API操作時(shí),系統(tǒng)調(diào)用會(huì)卡住。例如,我們調(diào)用read()方法從socket中去讀取數(shù)據(jù),但是其實(shí)我們無(wú)法預(yù)知對(duì)方數(shù)據(jù)是否發(fā)送,只能是一直等待或者網(wǎng)絡(luò)超時(shí)。這是我們傳統(tǒng)的阻塞式網(wǎng)絡(luò)編程,一個(gè)線程被掛起,望眼欲穿,什么事情也干不了,干等著。

想想看,要是有個(gè)幾千幾萬(wàn)的連接過(guò)來(lái),那么我們得啟動(dòng)對(duì)應(yīng)數(shù)量的線程大軍,全都hang住,這對(duì)操作系統(tǒng)來(lái)說(shuō)是災(zāi)難性的。線程越多,Context Switch操作越多,這會(huì)消耗大量CPU。
并且每一個(gè)線程都會(huì)使用一定大小的棧,要是有成千上萬(wàn)的線程,那么內(nèi)存消耗也是很?chē)樔说模m然可以通過(guò)大內(nèi)存撐起了上萬(wàn)并發(fā),那百萬(wàn),千萬(wàn)呢?不過(guò)系統(tǒng)穩(wěn)定性和成本都會(huì)指數(shù)爆炸。
也有人提出使用線程池去優(yōu)化,避免產(chǎn)生大量的線程,但是這樣做同時(shí)也限制了連接數(shù)量,這肯定不是最優(yōu)解,如果是一個(gè)對(duì)外的TCP服務(wù),網(wǎng)絡(luò)連接數(shù)量是不可預(yù)估的。
03.基于I/O復(fù)用模型的Reactor模式
當(dāng)當(dāng)當(dāng)~,NIO就要閃亮登場(chǎng),NON-Blocking,同步非阻塞模式,和BIO的區(qū)別是什么呢?
我們同樣進(jìn)行read操作:
BIO一個(gè)客戶(hù)端連接對(duì)應(yīng)一條處理線程,在沒(méi)有數(shù)據(jù)過(guò)來(lái)的時(shí)候,read操作就會(huì)一直卡在那里;
NIO不是這樣子的,一旦調(diào)用read()操作就會(huì)馬上返回,沒(méi)有數(shù)據(jù)就會(huì)返回-1,有數(shù)據(jù)就返回讀取的字節(jié)數(shù)。

一般我們會(huì)采用I/O多路復(fù)用的模型,多個(gè)連接共用一個(gè)對(duì)象,應(yīng)用程序只需要在一個(gè)對(duì)象上阻塞住,無(wú)需阻塞等待所有的連接。當(dāng)某條連接有新的數(shù)據(jù)可以處理的時(shí)候,就由系統(tǒng)通知應(yīng)用程序,阻塞的線程就會(huì)喚醒,準(zhǔn)備進(jìn)行業(yè)務(wù)處理。
這時(shí)候不會(huì)采用BIO中那種一個(gè)連接對(duì)應(yīng)一個(gè)線程的方式,我們采用線程池,復(fù)用線程資源,將連接完成的業(yè)務(wù)處理任務(wù)分發(fā)給線程池中的線程進(jìn)行處理,一個(gè)線程可以處理很多連接的業(yè)務(wù)。
基于線程池+I/O復(fù)用模型,這就是Reactor模式基本設(shè)計(jì)思想。
Reactor模式,通過(guò)一個(gè)或者多個(gè)輸入同時(shí)傳遞給服務(wù)端請(qǐng)求的事件驅(qū)動(dòng)處理模式。服務(wù)端處理多路請(qǐng)求,將請(qǐng)求分發(fā)給請(qǐng)求對(duì)應(yīng)的處理線程。I/O多路復(fù)用統(tǒng)一監(jiān)聽(tīng)多個(gè)事件,當(dāng)接收到事件的時(shí)候分發(fā)給處理線程。
04.Reactor模式的多樣變化
Reactor模式最基本的倆個(gè)組件:
- Reactor,反應(yīng)器,起反應(yīng)的人,他就像是電話(huà)接線員,接聽(tīng)來(lái)自各個(gè)地方的客戶(hù)電話(huà)并轉(zhuǎn)移到適當(dāng)?shù)穆?lián)系人。一般Reactor運(yùn)行在一個(gè)線程或者進(jìn)程上,負(fù)責(zé)監(jiān)聽(tīng)和分發(fā)事件,有事件到來(lái),就分發(fā)給適當(dāng)?shù)奶幚砭€程去對(duì)IO事件做出反應(yīng)。
- Handlers,操作者,就好比是公司里的業(yè)務(wù)員。處理執(zhí)行I/O事件要完成的實(shí)際事件,主要是Reactor調(diào)度給他,適當(dāng)?shù)貓?zhí)行非阻塞的操作。
根據(jù)Reactor的數(shù)量和處理資源線程池的數(shù)量不同我們可以有以下三種變化:
1.單Reactor單線程

2.單Reactor多線程

3.主從Reactor多線程

05.最后總結(jié)
最后,我們可以總結(jié)一下,Reactor模式具有如下的優(yōu)點(diǎn):
- 響應(yīng)快,不必為單個(gè)同步時(shí)間所阻塞,雖然Reactor本身依然是同步的
- 編程相對(duì)簡(jiǎn)單,可以最大程度的避免復(fù)雜的多線程及同步問(wèn)題,并且避免了多線程/進(jìn)程的切換開(kāi)銷(xiāo)
- 可擴(kuò)展性,可以方便的通過(guò)增加Reactor實(shí)例個(gè)數(shù)來(lái)充分利用CPU資源
- 可復(fù)用性,Reactor模型本身與具體事件處理邏輯無(wú)關(guān),具有很高的復(fù)用性