> 官网上( 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,也就事半功倍了!