了解nodejs中的事件輪詢(eventloop)

很久以前翻譯的,忘了出處(:з」∠)

首先需要知道的是,node.js的 I/O是異常昂貴的

The cost of I/O
L1-chache                               3 cycles
L2-cache                               14 cycles
RAM                                   250 cycles
Disk                             41000000 cycles
Network                         240000000 cycles

所以一旦當前的編程技術是通過等待I/O完成的話,那將是非常浪費的一件事情,現(xiàn)在有幾種方法可以處理對于性能的影響(可以參看異步套接字編程)

  1. 同步: 依次處理每個請求,并且每次只處理一個請求. 優(yōu)點: 非常的簡單,缺點: 一個請求就足以阻塞出整個應用
  2. 創(chuàng)建一個新的進程(fork a new process): 創(chuàng)建一個新的進程來處理新的請求. 優(yōu)點: 也很簡單,缺點: 多少個連接就意味著多少個進程,fork()是Unix程序員的錘子,因為所有問題看上就像個釘子,不過它通常都是多余的力量(overkill)
  3. 線程: 啟動一個新線程來處理請求. 優(yōu)點: 也很簡單,比起讓內(nèi)核(kernel)使用fork()來說要更加溫柔些,畢竟線程通常就有很多,而且資源開銷更小,缺點: 機器有可能并沒有好的線程程序,導致可能非常的復雜或降低速度,附送的還有對于共享資源的訪問也可能出現(xiàn)問題

其次,第二個問題是,線程共享的鏈接緩沖池,也是非常昂貴的

Apache 是多線程的,

每個請求都會生成一個線程或進程,它取決于conf,你可以看到這玩意是怎么吃內(nèi)存的,而且隨著并發(fā)連接數(shù)量的增多,所需要的線程也就越多,比如什么Nginx,節(jié)點等
而js并不是多線程,線程和進程會帶來沉重的內(nèi)存成本,所以它是基于事件的單線程,通過這樣來消除成千上萬的線程/進程,并且將所有需要處理的問題都綁定在一個線程上

Node.js 始終保持著單線程的優(yōu)良傳統(tǒng)

而它毫無疑問也是一個真正的單線程 : 在這里,你無法執(zhí)行任何的并行代碼,比如做一個延遲(sleep)來阻止服務器一秒:

while(new Date().getTime() < now + 1000) { 
// do nothing
 }

而在這期間,js不會回應任何來自客戶的請求,因為它只有一個線程執(zhí)行的代碼,或者如果你會一些別的什么cpu什么的代碼,說: 給我搞搞圖片大小,就算這樣,js依然會阻止這些請求

不過,不管什么都好,都是離不開并行的,尤其是你的代碼

代碼是沒辦法并行的運行在單個請求上的,不過,所幸所有的I/O都是一個事件并且異步的,所以下面這種方法并不會阻塞服務器

 c.query(
   'SELECT SLEEP(20);',
   function (err, results, fields) {
     if (err) {
       throw err;
     }
     res.writeHead(200, {'Content-Type': 'text/html'});
     res.end('<html><head><title>Hello</title></head><body><h1>Return from async DB query</h1></body></html>');
     c.end();
    }
);

如果你在一個請求中執(zhí)行了該操作,那么在這個數(shù)據(jù)庫處于阻塞(sleep)狀態(tài)的時候,你依然可以處理其他請求

這種方法可以很好的解決當你需要從同步走向異步/并發(fā)執(zhí)行時的問題

具有同步執(zhí)行時好的,因為它簡化了代碼(相對于并發(fā)問題,它可能會傾向于WTFS線程問題)
(Having synchronous execution is good, because it simplifies writing code (compared to threads, where concurrency issues have a tendency to result in WTFs).)

在node.js中,當你正在處理I/O的時候,不應該擔心后臺的問題,解決好你的回調(diào)問題,還有保證你的代碼不會被打斷,因為I/O并不會阻止其他請求,所以也不必承擔線程/進程的請求成本(比方說在Apache中的內(nèi)存開銷)

異步I/O是非常不錯的,因為I/O比大多數(shù)的代碼開銷更大,更昂貴,所以我們應該在等待I/O(阻塞過程)的過程中做更多的事情
(這個機制很大程度上是源于js本身就是一個非阻塞I/O)

事件輪詢(eventloop)"一個解決和處理外部事件時將它們轉(zhuǎn)換為回調(diào)函數(shù)的調(diào)用的實體(entity)",所以當代碼調(diào)用一個I/O的時候,node.js可以從這個請求切換到另一個請求,當調(diào)用I/O的時候,代碼將會保存回調(diào)并且返回某些結(jié)果給node.js運行環(huán)境中,只有當數(shù)據(jù)實際可用的時候(或者說不那么阻塞了(sleep事件過了等)),回調(diào)便會被調(diào)用

當然,在后臺中,會有來自DB的線程和進程及其他進程在訪問,然而,這些都沒有明確的暴露給你的代碼,所以你大可以不必擔心來自其他I/O的干擾或相互作用,比方說,并不需要在意像數(shù)據(jù)庫或者別的異步進程這些,因為從請求的角度看來,這些線程的結(jié)構(gòu)都是通過事件輪詢返回到你的代碼中的,相比起Apache模型,少了許多線程和連接的開銷,因為線程不需要遍歷每個連接,只有當你必須要使用其他的并行操作等,哪怕這服務器管理是node.js,也阻止不了你

除了I/O調(diào)用外,node.js希望所有的請求都能迅速返回,比如說像當cpu快速密集處理一堆請求后,應盡快分離掉這些進程,你可以與事件或是通過使用一個抽象的玩意比如WebWorkers互動,這很顯然意味著你無法將你的代碼通過事件脫離一個后臺的線程獨自并行.基本上所有的對象發(fā)出事件(比如EventEmitter的實例)都是支持異步事件的互動的,你可以使用這種方式,比如使用文件阻塞代碼交互,sockets或是子進程等,這些都是可以在node.js中與事件進行互動的,多核的機子可以使用這些方法,(可以查閱關于Http與Node)

內(nèi)部實現(xiàn)

在內(nèi)部,node.js依賴于測試程序提供的時間循環(huán),并輔以libeio采用混合線程來提供異步I/O,想知道更多的話,可以查閱下libev documentation
js內(nèi)部同樣有事件輪詢機制

那么我們要怎樣在node.js中使用異步?

Tim Caswell描述了這么個模式在這( 網(wǎng)址失效了)

  1. 第一類函數(shù)(First-class-functions).我們將函數(shù)作為參數(shù)傳出去,在需要的時候執(zhí)行他們就可以了
  2. 復合函數(shù)(Function composition).也就是所謂的匿名函數(shù)或者是當I/O這些事件執(zhí)行后回調(diào)的函數(shù)

請求會將所有的事件都放入到隊列中,而node.js會不斷詢問是否有事件,如果存在事件那么便會立即執(zhí)行,如果執(zhí)行過程中存在阻塞事件的話,那么node.js會將它放到一個專門的線程池中專門執(zhí)行這些操作

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末常空,一起剝皮案震驚了整個濱河市各谚,隨后出現(xiàn)的幾起案子尔崔,更是在濱河造成了極大的恐慌讼稚,老刑警劉巖弛姜,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異仇祭,居然都是意外死亡,警方通過查閱死者的電腦和手機猿挚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挪蹭,“玉大人亭饵,你說我怎么就攤上這事休偶×豪鳎” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵踏兜,是天一觀的道長词顾。 經(jīng)常有香客問我,道長碱妆,這世上最難降的妖魔是什么肉盹? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮疹尾,結(jié)果婚禮上上忍,老公的妹妹穿的比我還像新娘。我一直安慰自己纳本,他們只是感情好窍蓝,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著繁成,像睡著了一般吓笙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上巾腕,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天面睛,我揣著相機與錄音,去河邊找鬼尊搬。 笑死叁鉴,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的佛寿。 我是一名探鬼主播亲茅,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼狗准!你這毒婦竟也來了克锣?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤腔长,失蹤者是張志新(化名)和其女友劉穎袭祟,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捞附,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡巾乳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年您没,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胆绊。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡氨鹏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出压状,到底是詐尸還是另有隱情仆抵,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布种冬,位于F島的核電站镣丑,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏娱两。R本人自食惡果不足惜莺匠,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望十兢。 院中可真熱鬧趣竣,春花似錦、人聲如沸旱物。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽异袄。三九已至通砍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間烤蜕,已是汗流浹背封孙。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留讽营,地道東北人虎忌。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像橱鹏,于是被迫代替她去往敵國和親膜蠢。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

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