首页 > 解决方案 > Node.js 应用程序对 http 请求的延迟呈指数增长

问题描述

我有一个带有一个端点的 Express 应用程序,它从请求中获取一个字符串,将带有该字符串的请求发送到另一台服务器并返回响应。它能够很好地处理一些负载,但是一旦我开始启动它,延迟就会开始不受控制地增长并破坏整个应用程序。您可以在下面找到示例图,首先我尝试每分钟处理 60k 个请求,然后我只做了大约 5k,它在吞吐量和延迟方面都非常平滑。

我尝试使用内置https模块和axios库,两者都给出完全相同的结果。

我缺少一些设置吗?像最大并发请求或类似的东西。我对 Node 不太熟悉,所以对我所看到的有点困惑,特别是因为它是在完全不同的基础架构上复制的,同时在相同基础架构上的 Java 应用程序能够在没有任何问题的情况下每分钟执行 100k .

这是我用来发送请求的示例代码:

export const callService = axios => async request => {
  const params = {
    address: request.fullAddress
  }
  const response = await axios.get('endpoint', {
    params
  })
  return {
    result: response.data
  }
}

以下是我获取 axios 实例的方法,稍后将其传递给上面的柯里化函数:

const axiosInstance = axios.create({
  baseURL: SERVICE_BASE_URL,
  timeout: 10000,
})

我确实验证了一旦我删除了这个调用并只是模拟了响应,我可以使用更大的数字而没有延迟问题。

性能图

更新

今天用这段简单的代码做了额外的测试,根本没有Express服务器,只有这段代码。当我使用Array(1000)(有效地将并发请求计数设置为1000)时,它工作正常,只需几秒钟即可完成整个操作。但是,如果我尝试将其设置为10000,那么我会再次开始看到这些问题。可能值得一提的是,我正在使用 Catalina 的 Mac 上执行此操作,但是,当应用程序在 PCF 中运行时,会观察到相同的行为。代码:

const axiosInstance = Axios.create({
  baseURL: SERVICE_URL,
  httpAgent: new http.Agent({
    keepAlive: true
  })
})
const params = {
  // some query params
}
console.log(Date.now())
Promise.all([...Array(1000).keys()]
  .map(() => axiosInstance.get('path', {
    params
  })))
  .then(it => {
    console.log(Date.now())
  })

更新 2

看起来问题可能与 DNS 查找有关。我注意到,当我执行大量请求时,它会卡住一段时间,然后其中一个请求getaddrinfo ENOTFOUND失败,然后所有其他请求都因超时而失败。不知道如何解决这个问题

更新 3

设置得更高ulimit似乎有助于解决上一个问题,但是随着请求数量的增加,所有请求都开始超时。

标签: javascriptnode.jshttpsaxios

解决方案


这里的罪魁祸首似乎是您的系统用尽了可用的文件描述符(零星的 DNS 查找失败是这种情况的常见症状)。由于您已经排除了 Express 的相关性,我怀疑这是由于同时发出的出站请求:每个出站 API 调用都将打开一个到远程服务器的套接字(默认文件描述符限制通常为 1000)。

修复取决于您的确切操作系统,但在典型的 Unix 系统上,您可以检查用户进程的可用文件描述符:

ulimit -n

并用这样的方法提升它(或在 StackOverflow 中搜索“提升文件描述符限制”):

sudo ulimit -n 10000

注意:这里还有其他因素在起作用,正如原始问题的评论链中所述,许多不同的问题都会影响系统的吞吐量:内存压力、CPU 节流、劣质 HTTP 库等。


推荐阅读