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
Nov 08 NodeJs
NodeJS学习笔记之Connect中间件应用实例
Jan 27 NodeJs
解析NodeJs的调试方法
Dec 11 NodeJs
NodeJs测试框架Mocha的安装与使用
Mar 28 NodeJs
解析NodeJS异步I/O的实现
Apr 13 NodeJs
nodejs入门教程一:概念与用法简介
Apr 24 NodeJs
Express+Nodejs 下的登录拦截实现代码
Jul 01 NodeJs
nodejs中express入门和基础知识点学习
Sep 13 NodeJs
nodeJS进程管理器pm2的使用
Jan 09 NodeJs
关于NodeJS中的循环引用详解
Jul 23 NodeJs
Nodejs监控事件循环异常示例详解
Sep 22 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
PHP实现通用alert函数的方法
2015/03/11 PHP
phpstudy默认不支持64位php的解决方法
2017/02/20 PHP
Laravel 将数据表的数据导出,并生成seeds种子文件的方法
2019/10/09 PHP
javascript getElementsByClassName 和js取地址栏参数
2010/01/02 Javascript
javascript实现10个球随机运动、碰撞实例详解
2015/07/08 Javascript
聊一聊JS中this的指向问题
2016/06/17 Javascript
详解jquery validate实现表单验证 (正则表达式)
2017/01/18 Javascript
详解Vue 事件驱动和依赖追踪
2017/04/22 Javascript
jQuery实现鼠标滑过预览图片大图效果的方法
2017/04/26 jQuery
让网站自动生成章节目录索引的多个js代码
2018/01/07 Javascript
浅谈Node 调试工具入门教程
2018/03/20 Javascript
vue router+vuex实现首页登录验证判断逻辑
2018/05/17 Javascript
用Node提供静态文件服务的方法
2018/07/06 Javascript
小程序简单两栏瀑布流效果的实现
2019/12/18 Javascript
vue组件传值的实现方式小结【三种方式】
2020/02/05 Javascript
[03:36]2014DOTA2 TI小组赛综述 八强诞生进军钥匙球馆
2014/07/15 DOTA
pydev使用wxpython找不到路径的解决方法
2013/02/10 Python
Python3删除排序数组中重复项的方法分析
2019/01/31 Python
Python实现的在特定目录下导入模块功能分析
2019/02/11 Python
django echarts饼图数据动态加载的实例
2019/08/12 Python
Python 正则表达式爬虫使用案例解析
2019/09/23 Python
python发qq消息轰炸虐狗好友思路详解(完整代码)
2020/02/15 Python
tensorflow dataset.shuffle、dataset.batch、dataset.repeat顺序区别详解
2020/06/03 Python
基于Python中Remove函数的用法讨论
2020/12/11 Python
纯CSS3实现移动端展开和收起效果的示例代码
2020/04/26 HTML / CSS
html5指南-2.如何操作document metadata
2013/01/07 HTML / CSS
美国派对用品及装饰品网上商店:Shindigz
2016/07/30 全球购物
Schutz鞋官方网站:Schutz Shoes
2017/12/13 全球购物
性能测试工程师的面试题
2015/02/20 面试题
大学生职业生涯规划书模板
2014/01/03 职场文书
年终总结会议主持词
2014/03/17 职场文书
工作态度不端正检讨书
2014/10/04 职场文书
2015中秋节晚会主持词
2015/07/01 职场文书
《用字母表示数》教学反思
2016/02/17 职场文书
Python开发之QT解决无边框界面拖动卡屏问题(附带源码)
2021/05/27 Python
Win10玩csgo闪退如何解决?Win10玩csgo闪退的解决方法
2022/07/23 数码科技