NodeJs中的非阻塞方法介绍


Posted in NodeJs onJune 05, 2012

首先我们利用NodeJs先构建一个基本的服务器。
index.js

var requestHandler = require("./requestHandler"); 
var server = require("./server"); 
var route = { 
"/hello": requestHandler.hello, 
"/upload": requestHandler.upload 
}; 
server.start(route);

server.js
 
server.js
var http = require("http"); 
var url = require("url"); 
exports.start = function(route) { 
var server = http.createServer(function(req, res) { 
var pathName = url.parse(req.url).pathname; 
var handler = route[pathName]; 
if (handler) { 
console.log("Through path:" + pathName + ":" + new Date().getTime()); 
handler(res); 
} else { 
res.writeHead(404, {"Content-Type": "text/plain"}); 
res.end(); 
} 
}); 
server.listen(8088); 
};

requestHandler.js
exports.hello = function(res) { 
res.writeHead(200, {"Content-Type": "text/plain"}); 
res.write("say hello."); 
res.end(); 
}; 
exports.upload = function(res) { 
res.writeHead(200, {"Content-Type": "text/plain"}); 
res.write("upload"); 
res.end(); 
};

在cmd中,键入node index.js即可启动。
但是,上面的代码是阻塞的。如果在createServer的回调函数中,有花费长时间的计算。那么会阻塞node.js的事件轮询。
NodeJS中,他的高效,关键在于快速的返回事件循环。
我们将requestHandler.js改造如下,在这个例子中,由于事件循环一直被sleep函数阻塞着,导致createServer的callback无法及时返回。
function sleep(milliSecond) { 
var startTime = new Date().getTime(); 
console.log(startTime); 
while(new Date().getTime() <= milliSecond + startTime) { 
} 
console.log(new Date().getTime()); 
} 
exports.hello = function(res) { 
sleep(20000); 
res.writeHead(200, {"Content-Type": "text/plain"}); 
res.write("say hello."); 
res.end(); 
}; 
exports.upload = function(res) { 
res.writeHead(200, {"Content-Type": "text/plain"}); 
res.write("upload"); 
res.end(); 
};

那么先键入http://localhost:8088/hello,后键入http://localhost:8088/upload。你会发现,upload虽然不需要花费太多时间,但是却要等到hello完成。
我们试图找寻异步调用的方法。比如formidable中的上传,经测试是非阻塞的。查看formidable的源码,发现最关键的是下面的代码:
IncomingForm.prototype.parse = function(req, cb) { 
this.pause = function() { 
try { 
req.pause(); 
} catch (err) { 
// the stream was destroyed 
if (!this.ended) { 
// before it was completed, crash & burn 
this._error(err); 
} 
return false; 
} 
return true; 
}; 
this.resume = function() { 
try { 
req.resume(); 
} catch (err) { 
// the stream was destroyed 
if (!this.ended) { 
// before it was completed, crash & burn 
this._error(err); 
} 
return false; 
} 
return true; 
}; 
this.writeHeaders(req.headers); 
var self = this; 
req 
.on('error', function(err) { 
self._error(err); 
}) 
.on('aborted', function() { 
self.emit('aborted'); 
}) 
.on('data', function(buffer) { 
self.write(buffer); 
}) 
.on('end', function() { 
if (self.error) { 
return; 
} 
var err = self._parser.end(); 
if (err) { 
self._error(err); 
} 
}); 
if (cb) { 
var fields = {}, files = {}; 
this 
.on('field', function(name, value) { 
fields[name] = value; 
}) 
.on('file', function(name, file) { 
files[name] = file; 
}) 
.on('error', function(err) { 
cb(err, fields, files); 
}) 
.on('end', function() { 
cb(null, fields, files); 
}); 
} 
return this; 
};

在parse中,将head信息解析出来这段是阻塞的。但是真正上传文件却是在req.on(data)中,是利用了事件驱动,是非阻塞的。也就是说,他的非阻塞模型依赖整个nodeJS事件分派架构。
那么像sleep那样消耗大量计算,但是又不能依赖nodeJS分派架构的时候怎么办?
现在介绍一种,类似于html5 WebWorker的方法。
将requestHandler.js改造如下:
var childProcess = require("child_process"); 
exports.hello = function(res) { 
var n = childProcess.fork(__dirname + "/subProcess.js"); 
n.on('message', function() { 
res.writeHead(200, {"Content-Type": "text/plain"}); 
res.write("say hello."); 
res.end(); 
}); 
n.send({}); 
}; 
exports.upload = function(res) { 
res.writeHead(200, {"Content-Type": "text/plain"}); 
res.write("upload"); 
res.end(); 
};

并加入subProcess.js
function sleep(milliSecond) { 
var startTime = new Date().getTime(); 
console.log(startTime); 
while(new Date().getTime() <= milliSecond + startTime) { 
} 
console.log(new Date().getTime()); 
} 
process.on('message', function() { 
sleep(20000); 
process.send({}); 
});

测试,当hello还在等待时,upload已经返回。
结语:
大概在最近,我看了博客园上的很多NodeJs文章,大家都认为NodeJS是异步的。但是是何种程度的异步,这个概念就没有几篇文章讲对了。
其实NodeJS,他是一个双层的架构。C++,和javascript。并且是单线程的。这点尤其重要。Node其实是C++利用v8调用js命令,为了实现调用顺序维护了一个Event序列。因此,在一个js function内部,他的调用绝对会对其他的function产生阻塞。所以,网上所说的process.nextTick和setTimeout等,都不能够产生新的线程,以保证不被阻塞。他所实现的,不过是Event序列的元素顺序问题。 相对于setTimeout,process.nextTick的实现要简单的多,直接加入Event序列的最顶层(有个啥啥事件)。而setTimeout是增加了一个c++线程,在指定的时间将callback加入Event序列
以Node的file io为例。他的readFile等函数,第二个参数是一个callback。那么node中第一件事就是记录下callback,然后调用底层c++,调用c++开始的过程,你可以看成是异步的。因为那已经到了c++,而不是js这块。所以,exec到callback为止是异步的,http.createServer到触发callback为止是异步的。还有很多,比如mysql的调用方法,不知道大家有没有看过源码,他就是socket发送命令,相信这个过程速度非常快。然后等待回调的过程Node用c++隐藏了,他也是异步的。
而我这篇文章想说明的是,如果再js端有花费大量时间的运算怎么办。就用我上面所说的方法,用js打开c++的线程,这个subprocess.js,不在node的event序列内部维护。是新的线程,因此不会阻塞其他的js function
NodeJs 相关文章推荐
nodejs的10个性能优化技巧
Jul 15 NodeJs
轻松创建nodejs服务器(1):一个简单nodejs服务器例子
Dec 18 NodeJs
nodejs实现HTTPS发起POST请求
Apr 23 NodeJs
Windows 系统下设置Nodejs NPM全局路径
Apr 26 NodeJs
Nodejs 搭建简单的Web服务器详解及实例
Nov 30 NodeJs
用nodejs搭建websocket服务器
Jan 23 NodeJs
初探nodeJS
Jan 24 NodeJs
nodejs学习笔记之路由
Mar 27 NodeJs
Express+Nodejs 下的登录拦截实现代码
Jul 01 NodeJs
详解NODEJS基于FFMPEG视频推流测试
Nov 17 NodeJs
利用nodeJs anywhere搭建本地服务器环境的方法
May 12 NodeJs
Nodejs 识别图片类型的方法
Aug 15 NodeJs
nodejs win7下安装方法
May 24 #NodeJs
NodeJS的模块写法入门(实例代码)
Mar 07 #NodeJs
nodejs入门详解(多篇文章结合)
Mar 07 #NodeJs
NodeJS 模块开发及发布详解分享
Mar 07 #NodeJs
用nodejs访问ActiveX对象,以操作Access数据库为例。
Dec 15 #NodeJs
NodeJS框架Express的模板视图机制分析
Jul 19 #NodeJs
nodejs 后缀名判断限制代码
Mar 31 #NodeJs
You might like
松下Panasonic RF-B65电路分析
2021/03/02 无线电
ThinkPHP使用心得分享-ThinkPHP + Ajax 实现2级联动下拉菜单
2014/05/15 PHP
TP5框架实现上传多张图片的方法分析
2020/03/29 PHP
js event事件的传递与冒泡处理
2009/12/06 Javascript
原生javascript获取元素样式属性值的方法
2010/12/25 Javascript
为jquery的ajaxfileupload增加附加参数的方法
2014/03/04 Javascript
javascript消除window.close()的提示窗口
2015/05/20 Javascript
JavaScript变量的作用域全解析
2015/08/14 Javascript
JavaScript子窗口调用父窗口变量和函数的方法
2015/10/09 Javascript
D3.js实现直方图的方法详解
2016/09/25 Javascript
jQuery UI Grid 模态框中的表格实例代码
2017/04/01 jQuery
JS中精巧的自动柯里化实现方法
2017/12/12 Javascript
JavaScript满天星导航栏实现方法
2018/03/08 Javascript
vue实现的上拉加载更多数据/分页功能示例
2019/05/25 Javascript
vue将后台数据时间戳转换成日期格式
2019/07/31 Javascript
详解vue中使用axios对同一个接口连续请求导致返回数据混乱的问题
2019/11/06 Javascript
[02:23]2014DOTA2国际邀请赛中国战队回顾
2014/08/01 DOTA
[01:35:13]DOTA2-DPC中国联赛 正赛 DLG vs PHOENIX BO3 第一场 1月18日
2021/03/11 DOTA
Python 拷贝对象(深拷贝deepcopy与浅拷贝copy)
2008/09/06 Python
python使用装饰器和线程限制函数执行时间的方法
2015/04/18 Python
浅谈编码,解码,乱码的问题
2016/12/30 Python
Python入门_浅谈for循环、while循环
2017/05/16 Python
Python numpy 提取矩阵的某一行或某一列的实例
2018/04/03 Python
python3个性签名设计实现代码
2018/06/19 Python
css3实现针线缝合效果(图解步骤)
2013/02/04 HTML / CSS
英国男士时尚购物网站:Stuarts London
2017/10/22 全球购物
Pandora西班牙官方商店:PandoraShop.es
2020/10/05 全球购物
医科学校毕业生自荐信
2013/11/09 职场文书
澳大利亚商务邀请函
2014/01/17 职场文书
上级检查欢迎词
2014/01/18 职场文书
学习2014年全国两会心得体会
2014/03/12 职场文书
党员干部群众路线个人整改措施
2014/09/18 职场文书
企业财务总监岗位职责
2015/04/03 职场文书
iPhone13再次曝光
2021/04/15 数码科技
nginx作grpc的反向代理踩坑总结
2021/07/07 Servers
python数据分析之单因素分析线性拟合及地理编码
2022/06/25 Python