本節(jié)重點(diǎn)
- 掌握阻塞IO模型
本節(jié)時(shí)長(zhǎng)需控制在15分鐘內(nèi)
阻塞IO(blocking IO)
在linux中,默認(rèn)情況下所有的socket都是blocking,一個(gè)典型的讀操作流程大概是這樣:

當(dāng)用戶進(jìn)程調(diào)用了recvfrom這個(gè)系統(tǒng)調(diào)用,kernel就開(kāi)始了IO的第一個(gè)階段:準(zhǔn)備數(shù)據(jù)。對(duì)于network io來(lái)說(shuō),很多時(shí)候數(shù)據(jù)在一開(kāi)始還沒(méi)有到達(dá)(比如,還沒(méi)有收到一個(gè)完整的UDP包),這個(gè)時(shí)候kernel就要等待足夠的數(shù)據(jù)到來(lái)。
而在用戶進(jìn)程這邊,整個(gè)進(jìn)程會(huì)被阻塞。當(dāng)kernel一直等到數(shù)據(jù)準(zhǔn)備好了,它就會(huì)將數(shù)據(jù)從kernel中拷貝到用戶內(nèi)存,
然后kernel返回結(jié)果,用戶進(jìn)程才解除block的狀態(tài),重新運(yùn)行起來(lái)。
所以,blocking IO的特點(diǎn)就是在IO執(zhí)行的兩個(gè)階段(等待數(shù)據(jù)和拷貝數(shù)據(jù)兩個(gè)階段)都被block了。
幾乎所有的程序員第一次接觸到的網(wǎng)絡(luò)編程都是從listen\(\)、send\(\)、recv\(\) 等接口開(kāi)始的,
使用這些接口可以很方便的構(gòu)建服務(wù)器/客戶機(jī)的模型。然而大部分的socket接口都是阻塞型的。如下圖
ps:
所謂阻塞型接口是指系統(tǒng)調(diào)用(一般是IO接口)不返回調(diào)用結(jié)果并讓當(dāng)前線程一直阻塞
只有當(dāng)該系統(tǒng)調(diào)用獲得結(jié)果或者超時(shí)出錯(cuò)時(shí)才返回。

實(shí)際上,除非特別指定,幾乎所有的IO接口 ( 包括socket接口 ) 都是阻塞型的。這給網(wǎng)絡(luò)編程帶來(lái)了一個(gè)很大的問(wèn)題,如在調(diào)用recv(1024)的同時(shí),線程將被阻塞,在此期間,線程將無(wú)法執(zhí)行任何運(yùn)算或響應(yīng)任何的網(wǎng)絡(luò)請(qǐng)求。
一個(gè)簡(jiǎn)單的解決方案:
在服務(wù)器端使用多線程(或多進(jìn)程)。多線程(或多進(jìn)程)的目的是讓每個(gè)連接都擁有獨(dú)立的線程(或進(jìn)程),
這樣任何一個(gè)連接的阻塞都不會(huì)影響其他的連接。
該方案的問(wèn)題是:
開(kāi)啟多進(jìn)程或都線程的方式,在遇到要同時(shí)響應(yīng)成百上千路的連接請(qǐng)求,則無(wú)論多線程還是多進(jìn)程都會(huì)嚴(yán)重占據(jù)系統(tǒng)資源,
降低系統(tǒng)對(duì)外界響應(yīng)效率,而且線程與進(jìn)程本身也更容易進(jìn)入假死狀態(tài)。
改進(jìn)方案:
很多程序員可能會(huì)考慮使用“線程池”或“連接池”?!熬€程池”旨在減少創(chuàng)建和銷毀線程的頻率,
其維持一定合理數(shù)量的線程,并讓空閑的線程重新承擔(dān)新的執(zhí)行任務(wù)?!斑B接池”維持連接的緩存池,盡量重用已有的連接、
減少創(chuàng)建和關(guān)閉連接的頻率。這兩種技術(shù)都可以很好的降低系統(tǒng)開(kāi)銷,都被廣泛應(yīng)用很多大型系統(tǒng),如websphere、tomcat和各種數(shù)據(jù)庫(kù)等。
改進(jìn)后方案其實(shí)也存在著問(wèn)題:
“線程池”和“連接池”技術(shù)也只是在一定程度上緩解了頻繁調(diào)用IO接口帶來(lái)的資源占用。而且,所謂“池”始終有其上限,
當(dāng)請(qǐng)求大大超過(guò)上限時(shí),“池”構(gòu)成的系統(tǒng)對(duì)外界的響應(yīng)并不比沒(méi)有池的時(shí)候效果好多少。所以使用“池”必須考慮其面臨的響應(yīng)規(guī)模,
并根據(jù)響應(yīng)規(guī)模調(diào)整“池”的大小。
對(duì)應(yīng)上例中的所面臨的可能同時(shí)出現(xiàn)的上千甚至上萬(wàn)次的客戶端請(qǐng)求,“線程池”或“連接池”或許可以緩解部分壓力,但是不能解決所有問(wèn)題。總之,多線程模型可以方便高效的解決小規(guī)模的服務(wù)請(qǐng)求,但面對(duì)大規(guī)模的服務(wù)請(qǐng)求,多線程模型也會(huì)遇到瓶頸,可以用非阻塞接口來(lái)嘗試解決這個(gè)問(wèn)題。