添加许多异步作业时,Node.js变得非常慢。 这是一个示例:
const http = require("http")
http.createServer((req, res) => res.end("ok")).listen(8008)
const n = 100
let x = n
function f(){
for(let i = 0; i < n; i++){
http.get("http://localhost:8008", res => {
res.on("data", () => {})
res.on("end", e => --x || finish())
})
}
}
function finish(){
console.timeEnd("main")
process.exit()
}
f()
console.time("main")
创建服务器并向自身服务器请求。
结果在这里。
n: 100 -> 79ms
n: 1000 -> 3328ms
n: 10000 -> too long...
我尝试通过添加超时来分隔某些部分。
const http = require("http")
http.createServer((req, res) => res.end("ok")).listen(8008)
const n = 100
let x = n
async function f(){
for(let i = 0; i < n / 10; i++){
for(let j = 0; j < 10; j++){
http.get("http://localhost:8008", res => {
res.on("data", () => {})
res.on("end", e => --x || finish())
})
}
await new Promise(r => setTimeout(r, 0))
}
}
function finish(){
console.timeEnd("main")
process.exit()
}
f()
console.time("main")
结果在这里。
n: 100 -> 109ms
n: 1000 -> 850ms
n: 10000 -> 3969ms
此问题不仅限于http请求。
当我一次向数据库服务器发送许多INSERT查询时,我遇到了同样的问题。 从执行开始十分钟或更长时间后,我放弃并停止了该程序。 令人惊讶的是,没有记录插入到数据库中。 当时,没有使用任何交易。 我相信还没有执行INSERT处理。
node.js是否存在无法一次将许多作业添加到异步队列的问题?
我想知道为什么第一个示例比第二个示例要慢得多。 第二个示例显示服务器在大约4秒钟内处理了10000个请求。 但是,在第一个示例中,几分钟后它仍未完成。 第二个例子有延迟。 但是,我将超时设置为0毫秒,因此几乎意味着同时进行。
答案 0 :(得分:0)
Node对并发异步任务没有特定限制。这些任务的副作用可能会导致问题。成千上万的并发请求可能会使Web服务器或数据库服务器阻塞。这基本上是DDoS攻击。操作系统对最大允许连接的限制也可以适用。
应根据Web服务器的期望来计划异步任务。如果已知Web服务器可以正确处理此数量的并发请求,则可以根据经验选择延迟执行这些操作:
function doRequest() {
return new Promise((resolve, reject) => {
http.get("http://localhost:8008", res => {
res.on("data", () => {})
res.on("error", reject)
res.on("end", resolve)
});
});
}
function doDelay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function f(){
const requests = [];
for(let i = 0; i < n; i++) {
const request = doRequest();
requests.push(request);
await Promise.race([request, doDelay(100)]);
}
return Promise.all(requests);
}
或者可以限制并发请求的最大数量:
const maxConnections = 20;
async function f(){
const requests = [];
for(let i = 0; i < n; i++) {
if (requests.length > maxConnections) {
const requestIndex = await Promise.race(requests.map(
async (request, requestIndex) => {
await request;
return requestIndex;
}
));
requests.splice(requestIndex, 1);
}
const request = doRequest();
requests.push(request);
}
return Promise.all(requests);
}
请注意,第二个摘要f
中只能返回上一个请求的结果。