> 官網上( http://www.nodejs.org)給Node下的定義是:“一個搭建在Chrome JavaScript運行時
> 上的平臺,用于構建高速、可伸縮的網絡程序。 Node.js采用的事件驅動、非阻塞I/O模型,使它
> 既輕量又高效,并成為構建運行在分布式設備上的數據密集型實時程序的完美選擇。”
Node.js 是一個服務器端的、非阻斷式I/O的、事件驅動的JavaScript運行環境。
1. 服務器端JavaScript處理:server-side JavaScript execution
2. 非阻斷/異步I/O:non-blocking or asynchronous
3. I/O事件驅動:Event-driven
#### 理解Node.js的異步非阻塞I/O模型
今天.NET老師在課堂上吹捧多線程編程,我就想為單線程抱個不平,因為Node的單線程異步非阻塞I/O模型,演繹了單線程編程的神話。
##### 阻塞I/O
程序執行過程中必然要進行很多I/O操作,讀寫文件、輸入輸出、請求響應等等。I/O操作時最費時的,至少相對于代碼來說,在傳統的編程模式中,舉個例子,你要讀一個文件,整個線程都暫停下來,等待文件讀完后繼續執行。換言之,I/O操作阻塞了代碼的執行,極大地降低了程序的效率。
下面是是一個C#讀文件的例子:
~~~
private string ReadTxtToStr(string filename)
{
//打開文件,打開期間其他代碼停止執行,直到完成打開后繼續執行代碼。
FileStream fs = File.Open(filename, FileMode.Open);
Console.WriteLine("我被打開文件阻塞了。");
StreamReader sr = new StreamReader(fs);
//讀取文件,讀取期間其他代碼停止執行,直到完成讀取后繼續執行代碼。
string str=sr.ReadToEnd();
Console.WriteLine("我被讀取文件阻塞了。");
return str;
}
~~~
在上述代碼中,兩個Console.WriteLine()雖然會被執行,但是卻被無辜地阻塞一段時間。理論上,如果讀取這個文件需要10秒,我們就浪費了10秒在I/O等待中(實際程序運行中有很大一部分時間是浪費在I/O等待上的),在碼農眼里這可是天文數字。
> Having asynchronous I/O is good, because I/O is more expensive than most code and we should be doing something better than just waiting for I/O.
##### 非阻塞I/O
理解了阻塞I/O,非阻塞I/O就好理解。非阻塞I/O是程序執行過程中,I/O操作不會阻塞程序的執行,也就是在I/O操作的同時,繼續執行其他代碼(這得益于Node的事件循環機制)。在I/O設備效率還遠遠低于CPU效率的時代,這種I/O模型(非阻塞I/O)為程序帶來的性能上的提高是非常可觀的。
好,下面感受一下怎么用Node.js實現非阻塞I/O,繼續讀文件,看碼:
~~~
var fs = require("fs");
fs.readFile("./testfile", "utf8", function(error, file) {
if (error) throw error;
console.log("我讀完文件了!");
});
console.log("我不會被阻塞!");
~~~
復制上面代碼保存為test.js,并在同一目錄下新建一個名為testfile的文件,用node命令運行test.js,你將看到以下輸出:
> 我不會被阻塞!
> 我讀完文件了!
這顯然不符合傳統的程序執行順序,注意,這就是Node.js的非阻塞I/O了。
##### Node.js事件輪詢機制(event loop)
《Node入門》推薦我們去讀一下Mixu的一篇關于事件輪詢的博文,的確值得一讀,我英語一般,開著詞典還能勉強看,略懂吧。
Mixu說的最經典的一句話:
> Everything runs in parallel except your code!
(在Node中)除了代碼,一切都是并行的!
理解這句話,再去學Node,也就事半功倍了!