了解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完成的話,那將是非常浪費的一件事情,現在有幾種方法可以處理對于性能的影響(可以參看異步套接字編程)

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

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

Apache 是多線程的,

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

Node.js 始終保持著單線程的優良傳統

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

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

而在這期間,js不會回應任何來自客戶的請求,因為它只有一個線程執行的代碼,或者如果你會一些別的什么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();
    }
);

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

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

具有同步執行時好的,因為它簡化了代碼(相對于并發問題,它可能會傾向于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的時候,不應該擔心后臺的問題,解決好你的回調問題,還有保證你的代碼不會被打斷,因為I/O并不會阻止其他請求,所以也不必承擔線程/進程的請求成本(比方說在Apache中的內存開銷)

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

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

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

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

內部實現

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

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

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

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

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,002評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,400評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,136評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,714評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,452評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,818評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,812評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,997評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,552評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,292評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,510評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,721評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,121評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,429評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,235評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,480評論 2 379

推薦閱讀更多精彩內容