zl程序教程

您现在的位置是:首页 >  后端

当前栏目

NodeJs中的非阻塞方法介绍

Nodejs方法 介绍 阻塞
2023-06-13 09:14:34 时间
首先我们利用NodeJs先构建一个基本的服务器。
index.js
复制代码代码如下:

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

server.js
复制代码代码如下:


server.js
复制代码代码如下:
varhttp=require("http");
varurl=require("url");
exports.start=function(route){
varserver=http.createServer(function(req,res){
varpathName=url.parse(req.url).pathname;
varhandler=route[pathName];
if(handler){
console.log("Throughpath:"+pathName+":"+newDate().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("sayhello.");
res.end();
};
exports.upload=function(res){
res.writeHead(200,{"Content-Type":"text/plain"});
res.write("upload");
res.end();
};

在cmd中,键入nodeindex.js即可启动。
但是,上面的代码是阻塞的。如果在createServer的回调函数中,有花费长时间的计算。那么会阻塞node.js的事件轮询。
NodeJS中,他的高效,关键在于快速的返回事件循环。
我们将requestHandler.js改造如下,在这个例子中,由于事件循环一直被sleep函数阻塞着,导致createServer的callback无法及时返回。
复制代码代码如下:
functionsleep(milliSecond){
varstartTime=newDate().getTime();
console.log(startTime);
while(newDate().getTime()<=milliSecond+startTime){
}
console.log(newDate().getTime());
}
exports.hello=function(res){
sleep(20000);
res.writeHead(200,{"Content-Type":"text/plain"});
res.write("sayhello.");
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){
//thestreamwasdestroyed
if(!this.ended){
//beforeitwascompleted,crash&burn
this._error(err);
}
returnfalse;
}
returntrue;
};
this.resume=function(){
try{
req.resume();
}catch(err){
//thestreamwasdestroyed
if(!this.ended){
//beforeitwascompleted,crash&burn
this._error(err);
}
returnfalse;
}
returntrue;
};
this.writeHeaders(req.headers);
varself=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;
}
varerr=self._parser.end();
if(err){
self._error(err);
}
});
if(cb){
varfields={},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);
});
}
returnthis;
};

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

并加入subProcess.js
复制代码代码如下:
functionsleep(milliSecond){
varstartTime=newDate().getTime();
console.log(startTime);
while(newDate().getTime()<=milliSecond+startTime){
}
console.log(newDate().getTime());
}
process.on("message",function(){
sleep(20000);
process.send({});
});

测试,当hello还在等待时,upload已经返回。
结语:
大概在最近,我看了博客园上的很多NodeJs文章,大家都认为NodeJS是异步的。但是是何种程度的异步,这个概念就没有几篇文章讲对了。
其实NodeJS,他是一个双层的架构。C++,和javascript。并且是单线程的。这点尤其重要。Node其实是C++利用v8调用js命令,为了实现调用顺序维护了一个Event序列。因此,在一个jsfunction内部,他的调用绝对会对其他的function产生阻塞。所以,网上所说的process.nextTick和setTimeout等,都不能够产生新的线程,以保证不被阻塞。他所实现的,不过是Event序列的元素顺序问题。相对于setTimeout,process.nextTick的实现要简单的多,直接加入Event序列的最顶层(有个啥啥事件)。而setTimeout是增加了一个c++线程,在指定的时间将callback加入Event序列
以Node的fileio为例。他的readFile等函数,第二个参数是一个callback。那么node中第一件事就是记录下callback,然后调用底层c++,调用c++开始的过程,你可以看成是异步的。因为那已经到了c++,而不是js这块。所以,exec到callback为止是异步的,http.createServer到触发callback为止是异步的。还有很多,比如mysql的调用方法,不知道大家有没有看过源码,他就是socket发送命令,相信这个过程速度非常快。然后等待回调的过程Node用c++隐藏了,他也是异步的。
而我这篇文章想说明的是,如果再js端有花费大量时间的运算怎么办。就用我上面所说的方法,用js打开c++的线程,这个subprocess.js,不在node的event序列内部维护。是新的线程,因此不会阻塞其他的jsfunction