Javascript 对后端服务的节点服务器异步调用
我是Node的新手,正在编写我的第一个Node服务器。在调用后端rest服务之后,它应该用一个简单的页面响应一个简单的get请求 我正在使用express管理请求,并使用axios软件包进行后端请求。问题是服务器阻塞了事件循环,我在理解如何对后端进行异步调用时遇到了问题 到目前为止,前端服务器一次只能管理一个请求!!我预计,如果后端服务每次回答都需要10秒钟,那么前端服务器可以在10秒钟内回答两个并发请求,而不是在20秒钟内 我哪里错了? 以下是前端节点代码的摘录:Javascript 对后端服务的节点服务器异步调用,javascript,node.js,asynchronous,axios,event-loop,Javascript,Node.js,Asynchronous,Axios,Event Loop,我是Node的新手,正在编写我的第一个Node服务器。在调用后端rest服务之后,它应该用一个简单的页面响应一个简单的get请求 我正在使用express管理请求,并使用axios软件包进行后端请求。问题是服务器阻塞了事件循环,我在理解如何对后端进行异步调用时遇到了问题 到目前为止,前端服务器一次只能管理一个请求!!我预计,如果后端服务每次回答都需要10秒钟,那么前端服务器可以在10秒钟内回答两个并发请求,而不是在20秒钟内 我哪里错了? 以下是前端节点代码的摘录: app.get('/', f
app.get('/', function(req, res) {
//Making the call to the backend service. This should be asynchronous...
axios.post(env.get("BACKEND_SERVICE"),
{ "user": "some kind of input"})
.then(function(response){
//do somenthing with the data returned from the backend...
res.render('homepage');
})
}
app.post('/api/getTypes', jsonParser, function (req, res) {
console.log("> API request for 'api/getTypes' SLEEP");
var now = new Date().getTime();
while(new Date().getTime() < now + 10000){ /* do nothing */ }
console.log("> API request for 'api/getTypes' WAKE-UP");
res.json({"types":"1"});
}
这是后端节点代码的定义和提取:
app.get('/', function(req, res) {
//Making the call to the backend service. This should be asynchronous...
axios.post(env.get("BACKEND_SERVICE"),
{ "user": "some kind of input"})
.then(function(response){
//do somenthing with the data returned from the backend...
res.render('homepage');
})
}
app.post('/api/getTypes', jsonParser, function (req, res) {
console.log("> API request for 'api/getTypes' SLEEP");
var now = new Date().getTime();
while(new Date().getTime() < now + 10000){ /* do nothing */ }
console.log("> API request for 'api/getTypes' WAKE-UP");
res.json({"types":"1"});
}
app.post('/api/getTypes',jsonParser,function(req,res){
log(“>API请求'API/getTypes'睡眠”);
var now=new Date().getTime();
而(new Date().getTime()API请求'API/getTypes'唤醒”);
res.json({“types”:“1”});
}
问题在于,繁忙的等待会使后端服务器无法开始处理第二个请求
我假设您正在尝试模拟获取类型的过程需要一段时间。很可能您要做的是获取类型将是异步的和I/O绑定的(读取文件、查询数据库等)。要模拟这一过程,只需使用setTimeout
:
app.post('/api/getTypes', jsonParser, function (req, res) {
console.log("> API request for 'api/getTypes' SLEEP");
setTimeout(function() {
console.log("> API request for 'api/getTypes' WAKE-UP");
res.json({"types":"1"});
}, 10000);
});
这避免了占用后端服务器的唯一线程,使其可以自由地开始对第二个(第三个、第四个……)请求进行重叠处理
这是Node的关键原则之一:如果避免同步,就不要同步进行操作。:-)这就是API如此面向异步的原因
如果您确实在某个时候发现处理请求需要大量的CPU消耗,那么您可能会将其剥离为服务器的子进程,而不是在服务器进程中执行。节点的设计是单线程的,通过强调异步I/O实现了非常高的吞吐量。这对于您需要做的大多数事情来说都非常有效…直到它不起作用为止:-)
请回复您的评论: 后端进程将使用node以外的其他技术编写,它将调用一个DB,这可能需要一段时间。我编写了简单的节点rest服务来模拟这一点。我想了解的是,如果后端需要时间来处理请求,前端服务器将如何反应 花时间处理请求和占用唯一一个忙于等待的服务器线程(或执行大量CPU繁重的工作)之间有很大的区别。您的忙等待模型执行大量CPU繁重的工作,但如果获取类型是在节点外部,您就不会忙着等待它,而是将回调排队等待异步完成(等待子进程的I/O,或连接到第三个服务器进程的套接字的I/O,或等待DB的I/O,等等)。因此,上面的
setTimeout
示例是一个更好的模型,可以说明您真正在做什么
繁忙的等待使前端无法完成,因为它是这样的:
Backend
Time Frontend Queue Backend
−−−− −−−−−−−−−− −−−−−−−−−−−−−−−−−−−−− −−−−−−−−−−−−−−−−−−−−−
0 sec Request #1 −−−−−−> Receive request #1 −−−−−> Pick up job for request #1
0 sec Request #1 −−−−−−> Receive request #2
Busy wait 10 seconds
10 sec Got #1 back <−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− Send response #1
−−−−−> Pick up job for request #2
Busy wait 10 seconds
20 sec Got #2 back <−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− Send response #2
后端
时间前端队列后端
−−−− −−−−−−−−−− −−−−−−−−−−−−−−−−−−−−− −−−−−−−−−−−−−−−−−−−−−
0秒请求#1−−−−−−> 接收请求#1−−−−−> 按要求领取工作#1
0秒请求#1−−−−−−> 接收请求#2
忙等待10秒
10秒得到#1回<−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− 发送响应#1
−−−−−> 按要求领取工作#2
忙等待10秒
20秒得到2回<−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− 发送响应#2
因此,即使前端没有忙着等待,它也会看到20秒过去了,因为后端忙着为每个请求等待(无法做任何其他事情)10秒
但这不是你真正的设置会做的,除非你使用的其他技术也是单线程的。(如果是,您可能希望有多个类型并行运行。)是的,上面的代码只是为了模拟获取类型所需的后端过程。后端进程将使用node以外的其他技术编写,它将调用一个DB,这可能需要一段时间。我编写了简单的节点rest服务来模拟这一点。我想了解的是,如果后端需要时间来处理请求,前端服务器将如何反应。@Roberto:我已经在答案的末尾添加了内容。根据您的描述,在获取类型时,后端节点服务器不会陷入繁重的CPU工作。我还解释了为什么前端(不忙等待)会受到后端忙等待的影响。