问题是当您触发 1k-2k 传出 HTTP 请求时实际发生了什么?
我看到它可以通过 500 个连接轻松解决所有连接,但是从那里向上移动似乎会导致问题,因为连接保持打开状态并且 Node 应用程序将卡在那里。使用本地服务器 + 示例 Google 和其他模拟服务器进行测试。
因此,对于一些不同的服务器端点,我确实收到了原因:读取 ECONNRESET 很好,服务器无法处理请求并抛出错误。在 1k-2k 请求范围内,程序将挂起。当您使用 lsof -r 2 -i -a
检查打开的连接时你可以看到有一些 X 数量的连接一直卡在那里 0t0 TCP 192.168.0.20:54831->lk-in-f100.1e100.net:https (ESTABLISHED)
.当您向请求添加超时设置时,这些可能最终会出现超时错误,但为什么否则连接会永远保持下去并且主程序最终会处于某种不确定状态?
示例代码:
import fetch from 'node-fetch';
(async () => {
const promises = Array(1000).fill(1).map(async (_value, index) => {
const url = 'https://google.com';
const response = await fetch(url, {
// timeout: 15e3,
// headers: { Connection: 'keep-alive' }
});
if (response.statusText !== 'OK') {
console.log('No ok received', index);
}
return response;
})
try {
await Promise.all(promises);
} catch (e) {
console.error(e);
}
console.log('Done');
})();
最佳答案
为了确切了解发生了什么,我需要对您的脚本进行一些修改,但这里有。
首先,您可能知道如何node
和它的 event loop
有效,但让我快速回顾一下。运行脚本时,node
运行时首先运行它的同步部分,然后调度 promises
和 timers
在下一个循环中执行,当检查它们被解析时,在另一个循环中运行回调。这个简单的要点很好地解释了它,归功于@StephenGrider:
const pendingTimers = [];
const pendingOSTasks = [];
const pendingOperations = [];
// New timers, tasks, operations are recorded from myFile running
myFile.runContents();
function shouldContinue() {
// Check one: Any pending setTimeout, setInterval, setImmediate?
// Check two: Any pending OS tasks? (Like server listening to port)
// Check three: Any pending long running operations? (Like fs module)
return (
pendingTimers.length || pendingOSTasks.length || pendingOperations.length
);
}
// Entire body executes in one 'tick'
while (shouldContinue()) {
// 1) Node looks at pendingTimers and sees if any functions
// are ready to be called. setTimeout, setInterval
// 2) Node looks at pendingOSTasks and pendingOperations
// and calls relevant callbacks
// 3) Pause execution. Continue when...
// - a new pendingOSTask is done
// - a new pendingOperation is done
// - a timer is about to complete
// 4) Look at pendingTimers. Call any setImmediate
// 5) Handle any 'close' events
}
// exit back to terminal
Note that the event loop will never end until there is pending OS tasks. In other words, your node execution will never end until there is pending HTTP requests.
在您的情况下,它运行
async
函数,因为它总是返回一个 promise ,它会安排它在下一次循环迭代中执行。在你的异步函数上,你安排了另一个 1000 立即 promise (HTTP 请求) map
迭代。在那之后,您正在等待所有然后解决完成该程序。它肯定会起作用,除非您在 map
上使用匿名箭头函数不扔任何 错误 .如果你的一个 promise 抛出了一个错误而你没有处理它,一些 promise 将不会调用它们的回调,从而使程序运行到 结束 但不是到 退出 ,因为事件循环会阻止它退出,直到它解决所有任务,即使没有回调。正如 Promise.all
上所说的那样docs :它会在第一个 promise 拒绝后立即拒绝。所以,你在
ECONNRESET
错误与 Node 本身无关,与您的网络有关,它使提取引发错误,然后阻止事件循环结束。通过这个小修复,您将能够看到异步解决的所有请求:const fetch = require("node-fetch");
(async () => {
try {
const promises = Array(1000)
.fill(1)
.map(async (_value, index) => {
try {
const url = "https://google.com/";
const response = await fetch(url);
console.log(index, response.statusText);
return response;
} catch (e) {
console.error(index, e.message);
}
});
await Promise.all(promises);
} catch (e) {
console.error(e);
} finally {
console.log("Done");
}
})();
关于node.js - 触发并行 1k HTTP 请求会卡住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59778880/