La pregunta es ¿qué sucede realmente cuando desencadena solicitudes HTTP salientes 1k-2k? Veo que resolvería todas las conexiones fácilmente con 500 conexiones, pero avanzar desde allí parece causar problemas, ya que las conexiones se dejan abiertas y la aplicación Node se quedaría atascada allí. Probado con servidor local + ejemplo Google y otros servidores simulados.
Entonces, con algunos puntos finales de servidor diferentes , recibí una razón: lea ECONNRESET, que está bien, el servidor no pudo manejar la solicitud y arrojó un error. En el rango de solicitud 1k-2k, el programa simplemente se colgaría. Cuando verifica las conexiones abiertas lsof -r 2 -i -a
, puede ver que hay una cantidad X de conexiones que siguen colgadas allí 0t0 TCP 192.168.0.20:54831->lk-in-f100.1e100.net:https (ESTABLISHED)
. Cuando agrega la configuración de tiempo de espera a las solicitudes, esto probablemente termine con un error de tiempo de espera, pero ¿por qué de lo contrario la conexión se mantiene para siempre y el programa principal terminaría en algún estado de limbo?
Código de ejemplo:
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');
})();
fuente
npx envinfo
ejecución de su ejemplo en mi script Win 10 / nodev10.16.0 que termina en 8432.805msRespuestas:
Para entender lo que estaba sucediendo con seguridad, necesitaba hacer algunas modificaciones a su script, pero aquí están.
Primero, puede que sepa cómo
node
y cómoevent loop
funciona, pero permítame hacer un resumen rápido. Cuando ejecuta un script, elnode
tiempo de ejecución primero ejecuta la parte sincrónica del mismo, luego programa elpromises
ytimers
para que se ejecute en los siguientes bucles, y cuando se verifica que están resueltos, ejecute las devoluciones de llamada en otro bucle. Esta simple explicación lo explica muy bien, crédito a @StephenGrider:En su caso, ejecuta una
async
función, ya que siempre devolverá una promesa, programará su ejecución en la siguiente iteración del bucle. En su función asíncrona, programa otras 1000 promesas (solicitudes HTTP) a la vez en esamap
iteración. Después de eso, está esperando que todo se resuelva para finalizar el programa. Funcionará, seguro, a menos que su función de flecha anónimamap
no arroje ningún error . Si una de sus promesas arroja un error y no lo maneja, algunas de las promesas no recibirán su devolución de llamada para que el programa finalice pero no para salir , porque el bucle de eventos evitará que salga hasta que se resuelva todas las tareas, incluso sin devolución de llamada. Como dice en elPromise.all
docs : se rechazará tan pronto como se rechace la primera promesa.Por lo tanto, su
ECONNRESET
error no está relacionado con el nodo en sí, es algo con su red que hizo que la búsqueda arroje un error y luego evite que el bucle de eventos finalice. Con esta pequeña solución, podrá ver que todas las solicitudes se resuelven de forma asíncrona:fuente