【问题标题】:Node.js app exponentially growing latency for http requestsNode.js 应用程序对 http 请求的延迟呈指数增长
【发布时间】:2021-03-12 03:31:28
【问题描述】:

我有一个带有一个端点的 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 似乎有助于解决上一个问题,但随着请求数量的增加,所有请求都开始超时。

【问题讨论】:

  • 这个 axios 实例是为每个请求创建的,还是您在控制器中保留对它的引用并将其传递给 callService
  • @eol 只有一个实例
  • @LeonidBor DNS 故障看起来像是系统耗尽可用文件描述符的症状。这可能是由于传入或传出请求所致;每个出站 API 调用都将打开一个到远程服务器的套接字(默认文件描述符限制通常为 1000);或者,您的服务器可能从传入请求中打开了太多连接。无论哪种方式,您都可以尝试提高它(sudo ulimit -n 12288 可能——您可能需要根据您的操作系统采取额外的步骤)。
  • @LeonidBor 我不是 ulimit 专家,但我认为这是系统级别的硬限制。检查ulimit -n,我相信这是对用户级进程实施的实际文件描述符限制。
  • @RobertNubel 看起来设置ulimit 毕竟解决了问题,现在超时是由我使用的 Elastic APM 代理引起的。一旦我删除它,吞吐量就会大大增加。谢谢你的建议!

标签: javascript node.js https axios


【解决方案1】:

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

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

ulimit -n

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

sudo ulimit -n 10000

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-12
    • 1970-01-01
    • 2015-05-07
    • 1970-01-01
    相关资源
    最近更新 更多