【问题标题】:Why making many requests on NodeJS is slow?为什么在 NodeJS 上发出很多请求很慢?
【发布时间】:2021-07-17 16:18:18
【问题描述】:

我设置了一个本地快递服务器:

const express = require('express');
const app = express();
app.get('/test', (request, response) => {
    response.sendStatus(200);
});
const port = 3000;
app.listen(port, () => {});

然后我运行了一个脚本:

const axios = require('axios');
async function main() {
    console.time('time');
    const requests = Array(5000).fill().map(() => axios.get('http://localhost:3000/test'));
    await Promise.all(requests);
    console.timeEnd('time');
}
main();

我的问题是为什么这个脚本在我的机器上需要 3 秒?

我希望它需要几毫秒,就像任何其他 5000 次迭代的 for 循环一样。 因为我在本地运行服务器并通过 localhost 调用它,所以我预计不会有延迟,因此,promise 的等待时间应该几乎是 0。

谁能给我解释一下这是怎么回事?

另外,我怎样才能更快地同时处理多个请求?

编辑

看这里https://stressgrid.com/blog/webserver_benchmark/ 我希望我的单进程节点服务器能够无任何延迟地同时处理至少 20k 个请求。

所以我猜我的机器上缺少一些配置。启动节点服务器时可能有一些标志?

【问题讨论】:

  • 很有可能您不会一次触发 5000 个请求。可能有一个使用的连接轮询。快速服务器也可能具有有限数量的请求句柄。总而言之,我想说 3 秒对于 5000 个请求来说是相当快的。为什么需要它更快?
  • 那么,5000 个请求在 3 秒内?这是每个请求 0.6 毫秒。这有什么慢?请记住,nodejs 将您的 Javascript 作为单线程运行,因此每个请求都必须运行一些 http 服务器代码,然后是一些 Express 代码,然后是您的请求处理程序。并且只需要 0.6 毫秒就可以做到这一点。对于每个请求,还有一堆 TCP 堆栈代码在操作系统级别运行,但这可能在不同的线程中。而且,谁知道所有 Axios 在客户端都在做什么 - 在那里运行更多的单线程代码 - 其中一些将与其他请求并行,但仍然添加。
  • 螃蟹,3秒5000个请求,还是很快的
  • 0.6ms 对于现代系统中的纯开销来说相当慢

标签: node.js express axios


【解决方案1】:

三件事:

  • 该基准未正确设置。
  • Express 是所有 NodeJS Web 框架中最慢的。
  • 您的机器可能配置错误。

您可以在这里找到更好的基准测试和不同框架的比较:https://www.fastify.io/benchmarks/

他们的 github 存储库解释了他们所做的所有设置,因此您也可以将您的机器与他们的机器进行比较。

1.基准测试

说白了,你设置的基准是无效的。它不会重现任何现实世界的场景,并且没有针对它创建的合成场景进行优化。

只是为了举例说明,由于在 Node 上一切都是单线程的,因此串行运行请求的性能会更好,以便可以重用连接(还需要将您的请求框架更改为可以重用连接的框架)。如果您并行发出请求,HTTP 1 不会重用连接,而且您的客户端也没有设置为重用连接。

让我们看看修复后的结果。在我的机器上,您发布的基准测试甚至没有运行——如果您尝试在同一个端口上同时打开那么多连接,节点就会崩溃。此版本的理论性能与您的基准测试大致相同,并且可以运行:

const axios = require("axios");
async function main() {
  console.info(process.hrtime.bigint() / 1000000n + "ms");
  for (let i = 0; i < 5000; ++i) {
    await axios.get("http://localhost:3000/test");
  }

  console.info(process.hrtime.bigint() / 1000000n + "ms");
}
main();

这在我的机器上大约需要 3 秒(与您的机器差不多)。现在让我们重用连接:

const axios = require("axios");
const http = require("http");

async function main() {
  const httpAgent = new http.Agent({ keepAlive: true });

  console.info(process.hrtime.bigint() / 1000000n + "ms");
  for (let i = 0; i < 5000; ++i) {
    await axios.get("http://localhost:3000/test", { httpAgent });
  }

  console.info(process.hrtime.bigint() / 1000000n + "ms");
}
main();

这需要 800 毫秒。

您的基准测试遗漏了许多其他类似的细节。我无法总结所有这些。您可以将您的基准与 Fastify 的(上面链接)进行比较,以了解每个差异如何影响您的测量。

2.框架

Express 因其简单而广受欢迎,但它不是一个快速的框架。看看更现代的,例如 Koa 或 Fastify。请注意,您的应用程序可能会做的不仅仅是提供一个空白页面,因此您的 Web 框架的性能可能并不重要。也就是说,我不认为任何人都应该在 2021 年使用 express,如果他们有选择的话,因为他们的开发人员体验也已经过时(例如,不支持在中间件中等待请求)。

3.本地机器

也可能是您的计算机运行缓慢等原因。这是从重新运行标准化基准而不是创建自己的基准开始的另一个原因。

【讨论】:

  • 我试了 fastify,得到了同样的响应时间,差不多 3s。
  • @Rodrigo Ruiz 那么你知道你的节点服务器还不是你的瓶颈。
  • 我添加了更多细节,这样您至少可以在本地看到一些改进。
【解决方案2】:

从一开始就定义慢。您有Array(5000).fill(),我们可以将其解释为为我在内存中保留了 5000 个插槽,换句话说,您执行 5000 的 for 循环,然后执行 5000 请求,这意味着 10,000 循环。在 java 上做同样的 10,000 次循环并比较然后告诉我 JavaScript 是否很慢。 另外我不知道你有没有,但是 axios 有不少内部验证

【讨论】:

  • 5000 数组循环几乎是即时的。问题在于等待。即,由于某种原因,在发送或接收请求时,某些事情花费了太长时间(对于计算机标准)。如果您向 google.com 发出 5000 个请求,他们将在 300 毫秒内回复所有请求,这是互联网延迟,这在我的示例中不存在,因为我在本地运行。
  • Google 拥有价值 10 亿美元的基础设施,它会按需创建实例,另一方面,您有一台运行单个实例的 PC,因此当您拥有单个节点实例时,Google 会创建数百个服务器.不要比较两个,因为一个是一种语言,另一个是公司google!==node 用其他语言做同样的例子
猜你喜欢
  • 2015-08-29
  • 2017-01-29
  • 2020-06-05
  • 1970-01-01
  • 2013-03-24
  • 1970-01-01
  • 1970-01-01
  • 2014-08-14
  • 1970-01-01
相关资源
最近更新 更多