添加许多异步作业时,Node.js变得非常慢

时间:2018-11-03 07:37:12

标签: node.js

添加许多异步作业时,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毫秒,因此几乎意味着同时进行。

1 个答案:

答案 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中只能返回上一个请求的结果。