Вопрос в том, что на самом деле происходит, когда вы запускаете 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');
})();
источник
npx envinfo
, выполнение вашего примера на моем сценарии Win 10 / nodev10.16.0 заканчивается в 8432.805msОтветы:
Чтобы точно понять, что происходит, мне нужно было внести некоторые изменения в ваш скрипт, но здесь они есть.
Во-первых, вы можете знать, как
node
и как этоevent loop
работает, но позвольте мне сделать краткое резюме. Когда вы запускаете сценарий,node
среда выполнения сначала запускает его синхронную часть, а затем назначаетpromises
иtimers
выполняет следующие циклы, а когда проверяется, разрешены ли они, запускайте обратные вызовы в другом цикле. Это простое объяснение очень хорошо объясняет, спасибо @StephenGrider:В вашем случае он запускает
async
функцию, так как он всегда будет возвращать обещание, он будет планировать его выполнение в следующей итерации цикла. В вашей асинхронной функции вы запланируете еще 1000 обещаний (HTTP-запросов) одновременно на этойmap
итерации. После этого вы ждете, пока все решите закончить программу. Это сработает, если ваша анонимная функция стрелокmap
не выдаст ошибку . Если один из ваших обещаний выдает ошибку и не справиться с этим, некоторые из этих обещаний не будут их обратный вызов называется когда - либо делает программу закончить , но не выход , потому что цикл обработки событий не будет препятствовать его выходу , пока это не решает все задачи, даже без обратного вызова. Как говорится наPromise.all
документы : он будет отклонен, как только отклонится первое обещание.Таким образом, ваша
ECONNRESET
ошибка on не связана с самим узлом, это что-то с вашей сетью, которая произвела выборку, чтобы выдать ошибку и затем предотвратить завершение цикла обработки событий. С этим небольшим исправлением вы сможете увидеть все запросы, решаемые асинхронно:источник