Javascript Node.js/express:立即响应客户端请求并在nextTick中继续执行任务

Javascript Node.js/express:立即响应客户端请求并在nextTick中继续执行任务,javascript,node.js,asynchronous,express,Javascript,Node.js,Asynchronous,Express,我想将服务器高消耗CPU任务与用户体验分开: ./main.js: var express = require('express'); var Test = require('./resources/test'); var http = require('http'); var main = express(); main.set('port', process.env.PORT || 3000); main.set('views', __dirname + '/views'); main.u

我想将服务器高消耗CPU任务与用户体验分开:

./main.js:

var express = require('express');
var Test = require('./resources/test');
var http = require('http');
var main = express();

main.set('port', process.env.PORT || 3000);
main.set('views', __dirname + '/views');
main.use(express.logger('dev'));
main.use(express.bodyParser());
main.use(main.router);

main.get('/resources/test/async', Test.testAsync);

main.configure('development', function() {
  main.use(express.errorHandler());
});

http.createServer(main).listen(main.get('port'), function(){
  console.log('Express server app listening on port ' + main.get('port'));
});
./resources/test.js:

function Test() {}
module.exports = Test;

Test.testAsync = function(req, res) {
  res.send(200, "Hello world, this should be sent inmediately");
  process.nextTick(function() {
    console.log("Simulating large task");
    for (var j = 0; j < 1000000000; j++) {
      // Simulate large loop
    }
    console.log("phhhew!! Finished!");
  });
};
var child = require('child_process');
function Test() {}
module.exports = Test;

Test.testAsync = function(req, res) {
  res.send(200, "Hello world, this should be sent inmediately");
  var childTask = child.fork('child.js');
  childTask.send({ hello: 'world' });
};
函数测试(){}
module.exports=测试;
Test.testAsync=功能(请求、恢复){
res.send(200,“你好,世界,这应该立即发送”);
process.nextTick(函数(){
log(“模拟大型任务”);
对于(var j=0;j<100000000;j++){
//模拟大回路
}
log(“phhew!!完成了!”);
});
};
当请求“localhost:3000/resources/test/async”时,我希望浏览器呈现“Hello world,这应该立即发送”非常快,node.js将继续处理,并且在控制台中显示“finished”消息

相反,浏览器会一直等待node.js完成大型任务,然后呈现内容。我尝试了
res.set({'Connection':'close})
res.end()但没有按预期工作。我也用谷歌搜索过,但运气不好

如何将响应立即发送到客户端,服务器继续执行任务

编辑


在解决方案中发布fork方法

尝试等待,而不是占用CPU:

res.send("Hello world, this should be sent inmediately");
console.log("Response sent.");
setTimeout(function() {
  console.log("After-response code running!");
}, 3000);

node.js是单线程的。如果你用一个繁忙的循环锁定CPU,整个过程就会停止,直到完成为止。

Thakns对于Peter Lyons的帮助,最后主要的问题是firefox缓冲区:响应时间不够长,无法刷新它(因此firefox一直在等待)

无论如何,对于高CPU执行的任务,节点将保持挂起状态直到完成,所以不会处理新请求。如果有人需要它,可以通过分叉来实现(使用child_过程,请参见中的示例)

不得不说,通过分叉来改变上下文可能比用不同的记号分割任务花费更长的时间

./resources/test.js:

function Test() {}
module.exports = Test;

Test.testAsync = function(req, res) {
  res.send(200, "Hello world, this should be sent inmediately");
  process.nextTick(function() {
    console.log("Simulating large task");
    for (var j = 0; j < 1000000000; j++) {
      // Simulate large loop
    }
    console.log("phhhew!! Finished!");
  });
};
var child = require('child_process');
function Test() {}
module.exports = Test;

Test.testAsync = function(req, res) {
  res.send(200, "Hello world, this should be sent inmediately");
  var childTask = child.fork('child.js');
  childTask.send({ hello: 'world' });
};
./resources/child.js:

process.on('message', function(m) {
  console.log('CHILD got message:', m);
});

一个好的解决方案是使用
child\u process.fork()
:它允许您在不同的节点实例中执行应用程序的另一个JavaScript文件,从而在不同的事件循环中执行。当然,您仍然可以通过发送消息在两个进程之间进行通信:因此,您可以从UI进程向分叉进程发送消息,要求它执行某些操作

例如,在
ui.js
中:

var ChildProcess = require('child_process');
var heavyTaskWorker = ChildProcess.fork('./heavyTaskWorker.js');
...
var message = {
    operation: "longOperation1",
    parameters: {
        param1: "value1",
        ...
    }
};
heavyTaskWorker.send(message);
process.on('message', function (message) {
    switch (message.operation) {
    case 'longOperation1':
        longOperation1.apply(null, message.parameters);
        break;
    ...
    }
});
heavyTaskWorker.js
中:

var ChildProcess = require('child_process');
var heavyTaskWorker = ChildProcess.fork('./heavyTaskWorker.js');
...
var message = {
    operation: "longOperation1",
    parameters: {
        param1: "value1",
        ...
    }
};
heavyTaskWorker.send(message);
process.on('message', function (message) {
    switch (message.operation) {
    case 'longOperation1':
        longOperation1.apply(null, message.parameters);
        break;
    ...
    }
});
在这里测试,效果很好


希望有帮助

您是否尝试使用
res.end
而不是
res.send
是的,确实尝试了
res.set({'Connection':'close');res.send(200,“你好,世界,这应该立即发送”);res.end()谢谢,但我真正需要的是将intese CPU任务与用户体验分开。所以,当服务器继续处理请求时,用户得到的响应非常快。“处理”很好。正常的处理是一点CPU时间,然后是大量的I/O时间,节点可以很好地处理这些时间。Node不能处理所有CPU工作负载,如加密、图像处理等。您需要将CPU密集型工作负载转移到单独的进程,以便您的Node web服务器能够保持响应。我怀疑您并没有真正完成所有的CPU工作负载,您只是有一个误解,但如果是这样,node就不能在进程中完成这些工作。这就是我要寻找的:分叉。问题是,在一个响应速度非常高的服务器中,我希望在客户机烘焙后立即向其提供响应。响应时间是一个关键点:如果响应时间超出预期,客户端可以拒绝响应。所以我想回应一下,然后对数据库做一些工作。当我们谈论数千个QP时,这是最关键的,所以这里是分叉,甚至平衡。我原以为
process.nextTick(f)
会起作用,还打算尝试
setTimeout(f,0)
好吧,同样,如果您的工作负载是CPU密集型的,那么
process.nextTick
setTimeout
都不可行。您必须使用
child\u进程
模块生成独立的工作进程-独立的操作系统进程至关重要。或者您必须使用某种类型的工作负载排队系统,如zeromq、rabbitmq或类似系统。您可以提供一个分叉的工作示例吗?目前为止,我所接触到的最远的情况是,当尝试向child发送{hello:“world}消息时,得到的是“无法写入IPC通道”