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异步回调的优雅处理方法
Sep 25 NodeJs
NodeJS中利用Promise来封装异步函数
Feb 25 NodeJs
详解NodeJs支付宝移动支付签名及验签
Jan 06 NodeJs
nodejs中使用HTTP分块响应和定时器示例代码
Mar 19 NodeJs
nodejs个人博客开发第二步 入口文件
Apr 12 NodeJs
nodejs个人博客开发第四步 数据模型
Apr 12 NodeJs
详解Nodejs之静态资源处理
Jun 05 NodeJs
NodeJS链接MySql数据库的操作方法
Jun 27 NodeJs
Nodejs进阶之服务端字符编解码和乱码处理
Sep 04 NodeJs
Linux Centos7.2下安装nodejs&amp;npm配置全局路径的教程
May 15 NodeJs
nodejs 十六进制字符串型数据与btye型数据相互转换
Jul 30 NodeJs
nodejs文件夹深层复制功能
Sep 03 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实现统计网站在线人数的方法
2015/05/12 PHP
ThinkPHP打水印及设置水印位置的方法
2016/10/14 PHP
php实现的pdo公共类定义与用法示例
2017/07/19 PHP
PHP面向对象程序设计模拟一般面向对象语言中的方法重载(overload)示例
2019/06/13 PHP
PHP使用PDO、mysqli扩展实现与数据库交互操作详解
2019/07/20 PHP
javascript 一个自定义长度的文本自动换行的函数
2007/08/19 Javascript
jquery 表单进行客户端验证demo
2009/08/24 Javascript
手机端网页点击链接触发自动拨打或保存电话的示例代码
2014/08/15 Javascript
Node.js实现的简易网页抓取功能示例
2014/12/05 Javascript
JavaScript中property和attribute的区别详细介绍
2015/03/03 Javascript
js控制文本框输入的字符类型方法汇总
2015/06/19 Javascript
JavaScript中的prototype原型学习指南
2016/05/09 Javascript
JS判断鼠标进入容器的方向与window.open新窗口被拦截的问题
2016/12/23 Javascript
详解vue-cli中的ESlint配置文件eslintrc.js
2017/09/25 Javascript
原生JS实现的轮播图功能详解
2018/08/06 Javascript
vue中选项卡点击切换且能滑动切换功能的实现代码
2018/11/25 Javascript
JS实现的简单tab切换功能完整示例
2019/06/20 Javascript
[48:47]VGJ.S vs NB 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
简单谈谈python中的多进程
2016/11/06 Python
python多进程并行代码实例
2019/09/30 Python
Python小程序之在图片上加入数字的代码
2019/11/26 Python
python print 格式化输出,动态指定长度的实现
2020/04/12 Python
纯CSS3实现的井字棋游戏
2020/11/25 HTML / CSS
佛罗里达州印第安河新鲜水果:Hale Groves
2017/02/20 全球购物
台湾生鲜宅配:大口市集
2017/10/14 全球购物
英国在线定制百叶窗网站:Swift Direct Blinds
2020/02/25 全球购物
初中三好学生事迹材料
2014/01/13 职场文书
幼教简历自我评价
2014/01/28 职场文书
2014年三八妇女节活动总结
2014/03/01 职场文书
四议两公开实施方案
2014/03/28 职场文书
献爱心标语
2014/06/21 职场文书
2014年助理工程师工作总结
2014/11/14 职场文书
中学生清明节演讲稿
2015/03/18 职场文书
让人感觉高大上的讲话稿怎么写?
2019/07/08 职场文书
解决goland 导入项目后import里的包报红问题
2021/05/06 Golang
MySQL里面的子查询的基本使用
2021/08/02 MySQL