《深入淺出NodeJS》讀書筆記:Node的異步I/O

為什么要異步I/O杈笔?

消除UI阻塞序六,快速響應(yīng)資源

JavaScript是單線程的,它與UI渲染共用一個(gè)線程悠就。所以在JavaScript執(zhí)行的時(shí)候千绪,UI渲染將處于停頓的狀態(tài),用戶體驗(yàn)較差梗脾。而異步請求可以在下載資源的時(shí)候荸型,JavaScript和UI渲染都同時(shí)執(zhí)行,消除UI阻塞炸茧,降低響應(yīng)資源需要的時(shí)間開銷瑞妇。

單線程與多線程的優(yōu)缺點(diǎn)

單線程:

優(yōu)點(diǎn):易于表達(dá),符合編程人員的思維方式

缺點(diǎn):阻塞IO梭冠,性能差踪宠。

多線程:

優(yōu)點(diǎn):充分利用多核CPU資源

缺點(diǎn):創(chuàng)建線程和執(zhí)行期線程上下文切換的開銷較大,在復(fù)雜業(yè)務(wù)中妈嘹,多線程經(jīng)常面臨鎖柳琢、狀態(tài)同步的問題

Node的資源分配解決方案

利用單線程,遠(yuǎn)離多線程的死鎖润脸、狀態(tài)同步等問題柬脸;利用異步I/O,讓單線程遠(yuǎn)離阻塞毙驯,更好的利CPU

圖1.1-異步I/O調(diào)用示意圖

異步I/O與非阻塞I/O:

I/O的阻塞與非阻塞:IO對于操作系統(tǒng)內(nèi)核而言倒堕,只有阻塞與非阻塞兩種方式。阻塞模式的I/O會(huì)造成應(yīng)用程序等待爆价,直到I/O完成垦巴。同時(shí)操作系統(tǒng)也支持將I/O操作設(shè)置為非阻塞模式,這時(shí)應(yīng)用程序的調(diào)用將可能在沒有拿到真正數(shù)據(jù)時(shí)就立即返回了铭段,為此應(yīng)用程序需要多次調(diào)用才能確認(rèn)I/O操作完全完成骤宣。這種重復(fù)調(diào)用判斷操作是否完成的技術(shù)叫做“輪詢”。

I/O的同步與異步:I/O的同步與異步出現(xiàn)在應(yīng)用程序中序愚。如果做阻塞I/O調(diào)用憔披,應(yīng)用程序等待調(diào)用的完成的過程就是一種同步狀況。相反,I/O為非阻塞模式時(shí)芬膝,應(yīng)用程序則是異步的望门。

Node的異步I/O模型:

事件循環(huán)

進(jìn)程啟動(dòng)時(shí),Node會(huì)創(chuàng)建一個(gè)類似while(true)的循環(huán)锰霜,判斷是否有事件需要處理


觀察者

觀察者是用來判斷是否有事件需要處理筹误。事件循環(huán)中有一到多個(gè)觀察者,判斷過程會(huì)向觀察者詢問是否有需要處理的事件癣缅。

這個(gè)過程類似于飯店的廚師與前臺(tái)服務(wù)員的關(guān)系纫事。廚師每做完一輪菜,就會(huì)向前臺(tái)服務(wù)員詢問是否有要做的菜所灸,如果有就繼續(xù)做,沒有的話就下班了炫七。這一過程中爬立,前臺(tái)服務(wù)員就相當(dāng)于觀察者,她收到的顧客點(diǎn)單就是回調(diào)函數(shù)万哪。

事件循環(huán)是一個(gè)典型的生產(chǎn)者/消費(fèi)者模型侠驯。異步I/O、網(wǎng)絡(luò)請求是生產(chǎn)者奕巍,而事件循環(huán)則從觀察者那里取出事件并處理吟策。

請求對象

以fs.open( )方法為例

```

fs.open = function(path, flags, mode, callback) {

//...

binding.open(pathModule._makeLong(path),

stringToFlags(flags),

mode,

callback);

};

```


這個(gè)函數(shù)的作用是根據(jù)指定的路徑和參數(shù)去打開一個(gè)文件,從而得到一個(gè)文件描述符的止,是后續(xù)所有I/O操作的初始操作檩坚。


圖1.2-調(diào)用示意圖

整個(gè)調(diào)用過程:JavaScript -> Node核心模塊 -> C++內(nèi)建模塊 -> libuv系統(tǒng)調(diào)用

在uv_fs_open的調(diào)用過程中,Node.js創(chuàng)建了一個(gè)FSReqWrap請求對象诅福。從JavaScript傳入的參數(shù)和當(dāng)前方法都被封裝在這個(gè)請求對象中匾委,其中回調(diào)函數(shù)則被設(shè)置在這個(gè)對象的oncomplete_sym屬性上。

req_wrap->object_->Set(oncomplete_sym, callback);

對象包裝完畢后氓润,調(diào)用QueueUserWorkItem方法將這個(gè)FSReqWrap對象推入線程池中等待執(zhí)行赂乐。

```

QueueUserWorkItem(&uv_fs_thread_proc, req, WT_EXECUTELONGFUNCTION)

```


QueueUserWorkItem接受三個(gè)參數(shù),第一個(gè)是要執(zhí)行的方法咖气,第二個(gè)是方法的上下文挨措,第三個(gè)是執(zhí)行的標(biāo)志。

至此崩溪,由JavaScript層面發(fā)起的異步調(diào)用第一階段就此結(jié)束浅役。

執(zhí)行回調(diào)

組裝好請求對象,送入I/O線程池等待執(zhí)行伶唯,實(shí)際上完成了異步I/O的第一部分担租,回調(diào)通知是第二部分。

當(dāng)線程池中有可用線程的時(shí)候調(diào)用uv_fs_thread_proc方法執(zhí)行抵怎。該方法會(huì)根據(jù)傳入的類型調(diào)用相應(yīng)的底層函數(shù)奋救, ? ? 以uv_fs_open為例岭参,實(shí)際會(huì)調(diào)用到fs__open方法。調(diào)用完畢之后尝艘,會(huì)將獲取的結(jié)果設(shè)置在req->result上演侯。然后調(diào)用PostQueuedCompletionStatus通知我們的IOCP*對象操作已經(jīng)完成,并將線程歸還給線程池背亥。

```

PostQueuedCompletionStatus((loop)->iocp, 0, 0, &((req)->overlapped))

```


PostQueuedCompletionStatus方法的作用是向創(chuàng)建的IOCP上相關(guān)的線程通信秒际,線程根據(jù)執(zhí)行狀況和傳入的參數(shù)判定退出。

在這一過程中狡汉,每次事件循環(huán)會(huì)調(diào)用GetQueuedCompletionStatus()方法檢查線程池中是否有執(zhí)行完的請求娄徊,若有,會(huì)將請求對象加入到I/O觀察者的隊(duì)列中盾戴,將其作為事件處理寄锐。

I/O觀察者回調(diào)函數(shù)的行為就是取出請求對象的result屬性作為參數(shù),取出oncomplete_sym屬性作為方法尖啡,然后調(diào)用執(zhí)行橄仆,以此達(dá)到執(zhí)行回調(diào)函數(shù)的目的。


注:IOCP是windows下得異步I/O解決方案

總結(jié):JavaScript是單線程的衅斩,但Node本身其實(shí)是多線程的盆顾,除了用戶代碼無法并行執(zhí)行外,所有的I/O請求是可以并行執(zhí)行的畏梆。事件循環(huán)是Node異步I/O實(shí)現(xiàn)的核心您宪,Node通過事件驅(qū)動(dòng)的方式處理請求,使得其無須為每個(gè)請求創(chuàng)建額外的線程奠涌,省掉了創(chuàng)建和銷毀線程的開銷蚕涤。同時(shí)也應(yīng)為線程數(shù)較少,不受線程上下文切換的影響铣猩,維持了Node的高性能揖铜。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市达皿,隨后出現(xiàn)的幾起案子天吓,更是在濱河造成了極大的恐慌,老刑警劉巖峦椰,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件龄寞,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門想罕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屎蜓,“玉大人色解,你說我怎么就攤上這事茂嗓。” “怎么了科阎?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵述吸,是天一觀的道長。 經(jīng)常有香客問我锣笨,道長蝌矛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任错英,我火速辦了婚禮入撒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘椭岩。我一直安慰自己茅逮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布簿煌。 她就那樣靜靜地躺著,像睡著了一般鉴吹。 火紅的嫁衣襯著肌膚如雪姨伟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天豆励,我揣著相機(jī)與錄音夺荒,去河邊找鬼。 笑死良蒸,一個(gè)胖子當(dāng)著我的面吹牛技扼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嫩痰,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼剿吻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了串纺?” 一聲冷哼從身側(cè)響起丽旅,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎纺棺,沒想到半個(gè)月后榄笙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡祷蝌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年茅撞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡米丘,死狀恐怖剑令,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蠕蚜,我是刑警寧澤尚洽,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站靶累,受9級特大地震影響腺毫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜挣柬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一潮酒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧邪蛔,春花似錦急黎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至匠抗,卻和暖如春故源,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背汞贸。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工绳军, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人矢腻。 一個(gè)月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓门驾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親多柑。 傳聞我的和親對象是個(gè)殘疾皇子奶是,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容