设为首页 收藏本站
查看: 690|回复: 0

[经验分享] NodeJs中的非阻塞方法

[复制链接]

尚未签到

发表于 2017-2-22 11:59:42 | 显示全部楼层 |阅读模式
  首先我们利用NodeJs先构建一个基本的服务器。
  index.js



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

  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

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-345735-1-1.html 上篇帖子: 大熊君大话NodeJS之------Connect中间件模块(第一季) 下篇帖子: NodeJs:基于Provider模式的推送服务器实现
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表