您的位置:首页 > Web前端 > Node.js

欢迎进入Node.js世界

2015-03-11 22:12 253 查看
官网上(http://www.nodejs.org )给Node下的定义是:”一个搭建在Chrome JavaScript 运行时上的平台,用于构建高速、可伸缩的网络程序。Node.js采用的事件驱动、非阻塞I/O模型,使它既轻量又高效,并成为构建运行在分布式设备上的数据密集型实时程序的完美选择“。

异步和事件触发:浏览器

Node为服务端JavaScript提供了一个事件驱动的、异步的平台,它是一个服务器端的JavaScript解释器。了解浏览器的工作原理对我们了解Node的工作原理会有很大帮助。它们都是事件驱动(用事件轮询)和非阻塞的I/O处理(用异步I/O)。下面举个例子说明这是什么意思。

我们来看一小段jQuery 用XMLHttpRequest(XHR)做Ajax 请求的代码:

$.post('/resource.json',function(data){//I/O不会阻塞执行
console.log(data);
})
//脚本继续执行


这个程序会发送一个到resource.json的HTTP请求。当响应返回时会调用带着参数data的匿名函数(在这个上下文中的回调函数),data就是从那个请求中得到的数据。注意,代码没有写成下面这样:

var data=$.post('/resource.json');//在I/O完成之前程序会被阻塞
console.log(data);


在这个例子中,假定对resource.json的响应在准备好后会存储在变量data中,并且在此之前函数console.log 不会执行。I/O操作(Ajax 请求)会阻塞脚本继续执行,直到数据准备好。因为浏览器是单线程的,如果这个请求用了400ms 才返回,那么页面上的其他任何事件都要等到那之后才能执行。可以想象一下,如果一幅动画被停住了,或者用户试着跟页面交互时动不了,那种用户体验有多糟糕。理解JavaScript中的事件轮询

实际情况是,当浏览器中有I/O操作时,并且是用回调函数的方式写的。那么当这个I/O操作完成时,它会发出一个“事件”,然后这个事件会进入任务队列,等待CPU处理。

这个I/O是异步的,并且不会“阻塞”脚本执行,事件轮询仍然可以响应页面上执行的其他交互或请求。这样,浏览器可以对客户做出响应,并且可以处理页面上的很多交互动作。

异步和事件触发:服务器

可能大多数人都了解传统的服务器编程的I/O模型,下面是一个PHP的例子:

$result=mysql_query('select * from myTable');
print_r($result);


这段代码做了些I/O操作,并且在所有数据回来之前,这个进程会被阻塞。对于很多程序而言,这个模型没有什么问题,并且很容易理解。但有一点可能会被忽略:这个进程也有状态,或者说内存空间,并且在I/O完成之前基本上什么也不会做。根据I/O操作的延迟情况,那可能会有10ms到几分钟的时间。

对于服务器而言,当有更多的请求过来时,服务器通常会用多线程的方式,给每个连接分配一个线程。尽管这听起来是个很自然的委派服务器劳动力的方式,单程序内的线程管理会非常复杂。此外,当需要大量的线程处理很多并发的服务器连接时,线程会消耗额外的操作系统资源。线程需要CPU和额外的RAM来做上下文切换。

在Node中,I/O几乎总是在主事件轮询之外进行,使得服务器可以一直处于高效并且随时能够做出响应的状态。这样进程就更加不会受I/O限制,因为I/O延迟不会拖垮服务器,或者像在阻塞方式下那样占用很多资源。因此一些在服务器上曾经是重量级的操作,在Node服务器上仍然是可以轻量级的。

DIRT程序

实际上,Node所针对的应用程序有一个专门的简称:DIRT。它表示数据密集型实时(data-intensive real-time)程序。

var fs = require('fs');
fs.readFile('./resource.json',function(er,data){
console.log(data);
});


这段程序要从硬盘里读取resource.json文件。读取文件是异步任务,我们可以继续处理其他任何操作,直到数据准备好。这个不是在浏览器中用jQurey发起的一个Ajax请求,而是在Node中访问文件系统抓取resource.json。

下面是一个简单的HTTP服务器实现,它会用“Hello Word”响应所有请求:

var http = require('http');
http.createServer(function(req,res){
res.writeHead(200,{'Content-Type':'text/plain'});
res.end('Hello World\n');
}).listen(3000);
console.log('Server running at ' target='_blank'>http://localhost:300/');[/code] 
只要有请求过来,它就会把“Hello World”写入到响应中返回。这个事件模型跟浏览器中对onclick事件的监听类似。下面是服务器的另一种写法,这样看起来request事件更明显:

var http = require('http');
var server = http.createServer();
server.on('request',function(req,res){
res.writeHead(200,{'Content-Type':'text/plain'});
res.end('Hello World\n');
})
server.listen(3000);
console.log('Server running at ' target='_blank'>http://localhost:3000/');[/code] 
Node在数据流和数据流动上也很强大。你可以把数据流看成特殊的数组,只不过数组中的数据分散在空间上,而数据流中的数据是分散在时间上的。下面我们用数据流的方式来处理resource.json

var stream = fs.createReadStream('./resource.json');
stream.on('data',function(chunk){
console.log(chunk);
});
stream.on('end',function(){
console.log('finished');
});


只要有新的数据块准备好,就会激发data事件,当所有数据块都加载完之后,会激发一个end事件。下面看看如何把一张图片留到客户端:

var http = require('http');
var fs = require('fs');
http.createServer(function(req,res){
res.writeHead(200,{'Content-Type':'image/png'});
fs.createReadStream('./image.png').pipe(res);
}).listen(3000);
console.log('Server running at ' target='_blank'>http://localhost:3000/');[/code] 


在这行代码中,数据从文件中读出来(fs.createReadStream),然后数据随着进来就被送到(.pipe)客户端(res)。在数据流动时,事件轮询还能处理其他事件。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: