我有一个expressjs服务器在运行,端点init
在其中执行一些平均完成时间为10秒的密集操作。在这10秒内;"卡住";,使得无法向expressjs服务器发送请求。我已经在谷歌上搜索了一段时间,但没有发现任何能使expressjs同时处理请求的东西。如果这不可能的话,那就太傻了。对于任何提示或帮助,我都非常感谢。
示例代码:
routes.js
app.route('/v1/cv/random').get(init);
features/init.js
module.exports = async function init(req, res) {
try {
// perform some time consuming operation here
res.status(201).send(someVar);
} catch (err) {
res.status(500).send(`failed to init`).end();
}
};
可以以同步方式实现运行时间长的算法,例如河内塔:
function move(from, to, via, n) {
if (n > 1)
move(from, via, to, n - 1);
to.unshift(from.shift());
if (n > 1)
move(via, to, from, n - 1);
}
app.get("/tower", function(req, res) {
var a = [];
for (var i = 0; i < Number(req.query.n); i++) a.push(i);
var b = [];
var c = [];
move(a, b, c, a.length);
res.end("Done");
});
调用具有足够大的<N>
的GET /tower?n=<N>
确实会阻塞express的主循环。
这种阻塞可以通过在算法中引入异步性来避免,例如使用setTimeout(nextAlgorithmicStep)
命令。这将nextAlgorithmicStep
函数放入队列中,但同一队列也有处理并发请求的函数的空间:
function tick(from, to, via, n) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
move(from, to, via, n, resolve);
});
});
}
async function move(from, to, via, n, resolve) {
if (n > 1)
await tick(from, via, to, n - 1);
to.unshift(from.shift());
if (n > 1)
await tick(via, to, from, n - 1);
resolve();
}
app.get("/tower", async function(req, res) {
var a = [];
for (var i = 0; i < Number(req.query.n); i++) a.push(i);
var b = [];
var c = [];
await tick(a, b, c, a.length);
res.end("Done");
});
这样,您就可以(永远)等待请求GET /tower?n=64
的返回,但您至少仍然可以对同一服务器发出并发请求。(简单地使用Promise
或process.nextTick
而不是setTimeout
"不够异步"以允许在两者之间处理并发请求。)
然而,GET /tower?n=10
的执行完成了";立即";在第一个版本中,现在需要更长的时间。最好不要在所有的n
递归级别上使用setTimeout
,而是只在每十级左右使用一次。你必须在RSA算法中找到类似的异步优点。
这就是使用单线程Node.js程序可以做的。但是还有一种替代方案,它使用多个Node.js进程。
app.get("/tower", function(req, res) {
spawn("node", ["tower.js", req.query.n]).stdout.pipe(res);
});
其中tower.js
是附加的Javascript程序:
function move(from, to, via, n) {
if (n > 1)
move(from, via, to, n - 1);
to.unshift(from.shift());
if (n > 1)
move(via, to, from, n - 1);
}
var a = [];
for (var i = 0; i < Number(process.argv[2]); i++) a.push(i);
var b = [];
var c = [];
move(a, b, c, a.length);
process.stdout.write("Done");
在@Heiko Theißen更新他的答案前不久,我找到了答案。这是(我认为)类似的方法。
我找到了一种使用child_process
的方法,并通过使用来执行某个文件所拥有的一切
const {fork} = require('child_process');
...
module.exports = async function init(req, res) {
try {
const childProcess = fork('./path/to/the/script.js');
childProcess.send({'body': req.body});
childProcess.on('message', (message) => {
res.status(201).json({someVar: message}).end();
});
} catch (err) {
res.status(500).send(`failed to init`).end();
}
};
script.js
看起来像
process.on('message', async (message) => {
// perform a time consuming operation here
process.send(someVar);
process.exit();
});