【譯】理解node.js的事件輪詢

        在ManUel Kiessling的《The Node Beginner Book》 Node入門書(http://www.nodebeginner.org/index-zh-cn.html)中說明了Node.js的事件輪詢:

        Node.js可以在不新增額外線程的情況下,依然可以對任務進行並行處理 —— Node.js是單線程的。它通過事件輪詢(event loop)來實現並行操作,對此,我們應該要充分利用這一點 —— 儘可能的避免阻塞操作,取而代之,多使用非阻塞操作。

        然而,要用非阻塞操作,我們需要使用回調,通過將函數作爲參數傳遞給其他需要花時間做處理的函數...

        並提到Mixu的博文,《理解Node.js的事件輪詢》,下面就是這篇文章的譯文。

譯文來源:http://bbs.xyhtml5.com/thread-29-1-1.html

原文出處:http://blog.mixu.net/2011/02/01/understanding-the-node-js-event-loop/

關於Node.js的第一個基本概念是I/O操作開銷是巨大的:


所以,當前變成技術中最大的浪費來自於等待I/O操作的完成。有幾種方法可以解決性能的影響:
  同步方式:按次序一個一個的處理請求。利:簡單;弊:任何一個請求都可以阻塞其他所有請求。
  開啓新進程:每個請求都開啓一個新進程。利:簡單;弊:大量的鏈接意味着大量的進程。
  開啓新線程:每個請求都開啓一個新線程。利:簡單,而且跟進程比,對系統內核更加友好,因爲線程比進程輕的多;弊:不是所有的機器都支持線程,而且對於要處理共享資源的情況,多線程編程會很快變得太過於複雜。
  第二個基本概念是每個連接都創建一個新線程是很消耗內存的(例如:你可以對比Nginx回想一下Apache內存耗盡的情景)。
  Apache是多線程的:它爲每個請求開啓一個新的線程(或者是進程,這取決於你的配置),當併發連接增多時,你可以看看它是怎麼一點一點耗盡內存的。Nginx和Node.js不是多線程的,因爲線程的消耗太“重”了。它們兩個是單線程、基於事件的,這就把處理衆多連接所產生的線程/進程消耗給消除了。


  Node.js中你的代碼運行在單線程之中

確實只有一個線程:你不能並行執行任何代碼,比如:下面的“sleep”將會阻塞sever1秒鐘:

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

當這段代碼運行時,Node.js不會響應客戶端任何請求,因爲只有一個線程來運行你的代碼,另外,如果你執行cpu密集的任務,比如重設圖像的大小,它也會阻塞所有請求。


     …然而,除了你的代碼,其它的一切都是並行執行的

單線程沒法讓代碼並行執行。但是所有I/O操作都是事件驅動、並行的,所以下面的代碼不會阻塞server:

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時,其他請求也會立即被處理。


    爲什麼異步比較好?什麼時候我們應該從同步轉移到異步/並行執行呢?

同步執行也不錯,因爲它簡便了我們敲代碼。但在使用異步時,你不必關心後端是怎麼處理的。而且,在I/O操作時不會阻止其他請求,同時無需承擔每個請求所產生的線程/進程的成本。
  I/O操作時使用異步處理很好,因爲I/O操作的成本比單純執行代碼要高的多,我們應該在等待I/O時做其它更有意義的工作。


Event loop是指處理外部事件,並把外部事件轉換爲回調來進行調用的實體(晦澀難懂!!原文:an entity that handles and processes external events and converts them into callback invocations).所以,I/O調用的同時,server就可以去處理另一個請求。在一次I/O調用中,你的代碼保存回調函數並把控制權交回到node.js運行時。當數據加載完畢可以訪問時,就可以執行回調函數了。
  當然,在後端有很多數據庫接入和處理的進程。但是,這些都不需要通過你的代碼直接實現,你也就不必瞭解後臺I/O之間的相互作用關係。和Apache相比,省去了很多線程消耗,因爲不是每個鏈接都需要新線程,僅那些需要並行運行的才需要新線程。
  不只是I/O調用,Node.js期望所有的請求都能快速的響應,比如CPU密集的工作應該分離到其他進程,你可以使用事件和他交互。
  內部實現
  在內部,Node.js依賴libev來提供event loop,使用線程池來提供異步I/O。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章