前言
總括 :
- 原文地址:理解Node.js的事件輪詢
- Node小應用:Node-sample
智者閱讀群書,亦閱歷人生
正文
Node.js的兩個基本概念
Node.js的第一個基本概念就是I/O操作開銷是巨大的:
所以,當前變成技術中最大的浪費來自于等待I/O操作的完成。有幾種方法可以解決性能的影響:
- 同步方式:按次序一個一個的處理請求。利:簡單;弊:任何一個請求都可以阻塞其他所有請求。
- 開啟新進程:每個請求都開啟一個新進程。利:簡單;弊:大量的鏈接意味著大量的進程。
- 開啟新線程:每個請求都開啟一個新線程。利:簡單,而且跟進程比,對系統內核更加友好,因為線程比進程輕的多;弊:不是所有的機器都支持線程,而且對于要處理共享資源的情況,多線程編程會很快變得太過于復雜。
第二個基本概念是每個連接都創建一個新線程是很消耗內存的(例如:你可以對比Nginx回想一下Apache內存耗盡的情景)。
Apache是多線程的:它為每個請求開啟一個新的線程(或者是進程,這取決于你的配置),當并發連接增多時,你可以看看它是怎么一點一點耗盡內存的。Nginx和Node.js不是多線程的,因為線程的消耗太“重”了。它們兩個是單線程、基于事件的,這就把處理眾多連接所產生的線程/進程消耗給消除了。
單線程
確實只有一個線程:你不能并行執行任何代碼,比如:下面的“sleep”將會阻塞sever1秒鐘:
function sleep() {
var now = new Data().getTime();
while (new Date().getTime() < now + 1000) {
// do nothing
}
}
sleep();
但就我目前學習階段而言,我覺得好多人對于所謂的node單線程是有誤解的。實際上官方給出的“單線程”是具有誤導性的。所謂的單線程是指你的代碼只運行在一個線程上(好多地方都叫它主線程,實際上Javascript的瀏覽器運行環境不也是這么處理我們寫的Javascript代碼的嘛),而諸多任務的并行處理,就需要多線程了,如下圖:
如上圖,Node.js中的單線程之說指的就是這個主線程,這個主線程有一個循環結構,保持著整個程序(你寫的代碼)的運轉。
事件輪詢
其實上面我們所說的維持主線程運行的循環這部分就是"事件輪詢",它存在于主線程中,負責不停地調用開發者編寫的代碼。但對開發者是不可見的。so...開發者編寫的代碼是怎樣被調用的呢?看下圖:
如上圖,異步函數在執行結束后,會在事件隊列中添加一個事件(遵循先進先出原則),主線程中的代碼執行完畢后(即一次循環結束),下一次循環開始就在事件隊列中"讀取"事件,然后調用它所對應的回調函數(所以回調函數的執行順序是不一定的)。如果開發者在回調函數中調用了阻塞方法(比如上文中的sleep函數),那么整個事件輪詢就會阻塞,事件隊列中的事件得不到及時處理。正因為這樣,nodejs中的一些庫方法均是異步的,也提倡用戶調用異步方法。
var fs = require('fs');
fs.readFile('hello.txt', function (err, data) { //異步讀取文件
console.log("read file end");
});
while(1)
{
console.log("call readFile over");
}
如上代碼,我們雖然使用了異步方法readfile讀取文件,但read file end
永遠不會輸出,因為代碼始終在while循環中,下一次事件輪詢始終沒法開始,也就沒法'讀取'事件隊列調用相應的回調函數了。
最后有一個Node-sample是博主平時積累的一些代碼,包含注釋,匯總成了一個小應用,還是可以看到學習的蛛絲馬跡的。感興趣的您可以看看。
后記
參考文章: