Pytanie brzmi, co się dzieje, gdy uruchamiasz wychodzące żądania HTTP 1k-2k? Widzę, że rozwiązałoby to wszystkie połączenia z łatwością z 500 połączeniami, ale przejście w górę stamtąd wydaje się powodować problemy, ponieważ połączenia pozostają otwarte, a aplikacja Node utknie w tym miejscu. Testowane na serwerze lokalnym + przykład Google i innych fałszywych serwerach.
Więc z kilkoma różnymi punktami końcowymi serwera otrzymałem powód: przeczytaj ECONNRESET, co jest w porządku, serwer nie mógł obsłużyć żądania i zgłosić błąd. W zakresie żądań 1k-2k program po prostu się zawiesił. Gdy sprawdzisz otwarte połączenia lsof -r 2 -i -a
, zobaczysz, że istnieje pewna liczba połączeń X, które się tam zawieszają 0t0 TCP 192.168.0.20:54831->lk-in-f100.1e100.net:https (ESTABLISHED)
. Gdy dodasz ustawienie limitu czasu do żądań, prawdopodobnie skończy się to błędem przekroczenia limitu czasu, ale dlaczego inaczej połączenie będzie utrzymywane na zawsze, a główny program skończy w stanie zawieszenia?
Przykładowy kod:
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');
})();
źródło
npx envinfo
działania twojego przykładu na moim skrypcie Win 10 / nodev10.16.0 kończy się na 8432.805msOdpowiedzi:
Aby na pewno zrozumieć, co się dzieje, musiałem wprowadzić pewne zmiany w skrypcie, ale oto są.
Po pierwsze, możesz wiedzieć, jak
node
i jak toevent loop
działa, ale pozwól mi szybko podsumować. Po uruchomieniu skryptunode
środowisko wykonawcze najpierw uruchamia jego synchroniczną część, a następnie planuje wykonaniepromises
itimers
do wykonania w następnych pętlach, a po zaznaczeniu, że zostały one rozwiązane, uruchom wywołania zwrotne w innej pętli. Ta prosta treść wyjaśnia to bardzo dobrze, podziękowania dla @StephenGrider:W twoim przypadku uruchamia
async
funkcję, ponieważ zawsze zwróci obietnicę, zaplanuje wykonanie w następnej iteracji pętli. W funkcji asynchronicznej planujesz kolejne 1000 obietnic (żądań HTTP) jednocześnie w tejmap
iteracji. Następnie czekasz na wszystko, a następnie zdecydujesz się zakończyć program. Na pewno zadziała, chyba że twoja anonimowa funkcja strzałkimap
nie zgłasza żadnego błędu . Jeśli jeden z twoich obietnic zgłasza błąd i nie poradzić, niektóre z obietnic nie będzie miał ich zwrotna nazywa kiedykolwiek dzięki czemu program do zakończenia , ale nie do wyjścia , bo pętla zdarzenie uniemożliwi jej wyjściu aż rozwiązuje wszystkie zadania, nawet bez oddzwaniania. Jak napisano wPromise.all
dokumenty : odrzuci, gdy tylko pierwsza obietnica odrzuci.Twój
ECONNRESET
błąd nie jest związany z samym węzłem, jest czymś w twojej sieci, która spowodowała, że pobieranie zwróciło błąd, a następnie uniemożliwiło zakończenie pętli zdarzeń. Dzięki tej małej poprawce można zobaczyć, że wszystkie żądania są rozwiązywane asynchronicznie:źródło