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创建web服务器之hello world程序
Aug 20 NodeJs
NodeJS与HTML5相结合实现拖拽多个文件上传到服务器的实现方法
Jul 26 NodeJs
nodejs和php实现图片访问实时处理
Jan 05 NodeJs
nodejs实现发出蜂鸣声音(系统报警声)的方法
Jan 18 NodeJs
nodejs中使用HTTP分块响应和定时器示例代码
Mar 19 NodeJs
深入nodejs中流(stream)的理解
Mar 27 NodeJs
使用 NodeJS+Express 开发服务端的简单介绍
Apr 07 NodeJs
Nodejs实现文件上传的示例代码
Sep 26 NodeJs
详解NodeJs开发微信公众号
May 25 NodeJs
nodejs 使用 js 模块的方法实例详解
Dec 04 NodeJs
NodeJs使用webpack打包项目的方法详解
Feb 28 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函数常用用法小结
2010/02/08 PHP
PHP和Mysqlweb应用开发核心技术 第1部分 Php基础-3 代码组织和重用2
2011/07/03 PHP
PHP限制页面只能在微信自带浏览器访问的代码
2014/01/15 PHP
基于JQuery的密码强度验证代码
2010/03/01 Javascript
js弹窗返回值详解(window.open方式)
2014/01/11 Javascript
通过pjax实现无刷新翻页(兼容新版jquery)
2014/01/31 Javascript
js在数组中删除重复的元素自保留一个(两种实现思路)
2014/08/22 Javascript
javascript的tab切换原理与效果实现方法
2015/01/10 Javascript
Js和JQuery获取鼠标指针坐标的实现代码分享
2015/05/25 Javascript
jQuery validate+artdialog+jquery form实现弹出表单思路详解
2016/04/18 Javascript
loading动画特效小结
2017/01/22 Javascript
Vue动态组件实例解析
2017/08/20 Javascript
微信禁止下拉查看URL的处理方法
2017/09/28 Javascript
js实现图片粘贴上传到服务器并展示的实例
2017/11/08 Javascript
jquery实现搜索框功能实例详解
2018/07/23 jQuery
vue实现的上拉加载更多数据/分页功能示例
2019/05/25 Javascript
angularjs自定义过滤器demo示例
2019/08/24 Javascript
layui自定义验证,用ajax查询后台是否有重复数据,form.verify的例子
2019/09/06 Javascript
vue中的使用token的方法示例
2020/03/10 Javascript
python if not in 多条件判断代码
2016/09/21 Python
python numpy函数中的linspace创建等差数列详解
2017/10/13 Python
详解用python实现简单的遗传算法
2018/01/02 Python
Python并发之多进程的方法实例代码
2018/08/15 Python
使用memory_profiler监测python代码运行时内存消耗方法
2018/12/03 Python
深度辨析Python的eval()与exec()的方法
2019/03/26 Python
Python 写入训练日志文件并控制台输出解析
2019/08/13 Python
tensorflow从ckpt和从.pb文件读取变量的值方式
2020/05/26 Python
在django中查询获取数据,get, filter,all(),values()操作
2020/08/09 Python
canvas实现有递增动画的环形进度条的实现方法
2019/07/10 HTML / CSS
AOP的定义以及作用
2013/09/08 面试题
中文系学生自荐信范文
2013/11/13 职场文书
创新社会管理心得体会
2014/09/12 职场文书
爱岗敬业事迹材料
2014/12/24 职场文书
芙蓉镇观后感
2015/06/10 职场文书
一文带你理解vue创建一个后台管理系统流程(Vue+Element)
2021/05/18 Vue.js
Spring Boot优化后启动速度快到飞起技巧示例
2022/07/23 Java/Android