【发布时间】: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