La question est de savoir ce qui se passe réellement lorsque vous déclenchez des requêtes HTTP sortantes 1k-2k? Je vois que cela résoudrait toutes les connexions facilement avec 500 connexions mais le déplacement vers le haut à partir de là semble causer des problèmes car les connexions sont laissées ouvertes et l'application Node y serait bloquée. Testé avec serveur local + exemple Google et autres serveurs fictifs.
Donc, avec certains points de terminaison de serveur différents, j'ai reçu une raison: lisez ECONNRESET, ce qui est bien, le serveur n'a pas pu gérer la demande et générer une erreur. Dans la plage de requêtes 1k-2k, le programme se bloque simplement. Lorsque vous vérifiez les connexions ouvertes avec, lsof -r 2 -i -a
vous pouvez voir qu'il y a une quantité X de connexions qui y restent 0t0 TCP 192.168.0.20:54831->lk-in-f100.1e100.net:https (ESTABLISHED)
. Lorsque vous ajoutez un paramètre de délai d'attente aux demandes, cela aboutirait probablement à une erreur de délai d'attente, mais pourquoi sinon la connexion est maintenue indéfiniment et le programme principal se retrouverait dans un état limbo?
Exemple de code:
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');
})();
la source
npx envinfo
exécution de votre exemple sur mon script Win 10 / nodev10.16.0 se termine en 8432.805msRéponses:
Pour comprendre ce qui se passait à coup sûr, j'ai dû apporter quelques modifications à votre script, mais les voici.
Tout d'abord, vous savez peut-être comment
node
et sonevent loop
fonctionnement, mais permettez-moi de faire un bref résumé. Lorsque vous exécutez un script, lenode
runtime exécute d'abord la partie synchrone de celui-ci, puis planifie lepromises
ettimers
à exécuter sur les boucles suivantes et, une fois vérifiés, ils sont résolus, exécutez les rappels dans une autre boucle. Ce simple résumé l'explique très bien, crédit à @StephenGrider:Dans votre cas, il exécute une
async
fonction, car il retournera toujours une promesse, il planifiera son exécution dans la prochaine itération de la boucle. Sur votre fonction asynchrone, vous planifiez 1000 autres promesses (requêtes HTTP) à la fois dans cettemap
itération. Après cela, vous attendez tout, puis être résolu pour terminer le programme. Cela fonctionnera, bien sûr, à moins que votre fonction de flèche anonyme sur lemap
ne génère aucune erreur . Si l' un de vos promesses renvoie une erreur et vous ne manipulez pas, certaines des promesses ne génèreront pas de rappel appelé jamais rendre le programme à la fin , mais pas à la sortie , car la boucle d'événements qui l' empêchera de sortie jusqu'à ce qu'il décide toutes les tâches, même sans rappel. Comme il est dit sur lePromise.all
docs : il sera rejeté dès que la première promesse sera rejetée.Ainsi, votre
ECONNRESET
erreur d' erreur n'est pas liée au nœud lui-même, c'est quelque chose avec votre réseau qui a effectué la récupération pour générer une erreur, puis empêcher la boucle d'événement de se terminer. Avec ce petit correctif, vous seriez en mesure de voir toutes les demandes résolues de manière asynchrone:la source