2013-03-20 13 views
10

Rozważmy następujący prosty node.js Zastosowanie:Węzeł js - http.request() problemy z zestawianiem połączeń

var http = require('http'); 
http.createServer(function() { }).listen(8124); // Prevent process shutting down 

var requestNo = 1; 
var maxRequests = 2000; 

function requestTest() { 
    http.request({ host: 'www.google.com', method: 'GET' }, function(res) { 
     console.log('Completed ' + (requestNo++)); 

     if (requestNo <= maxRequests) { 
      requestTest(); 
     } 
    }).end(); 
} 

requestTest(); 

To sprawia 2000 żądań HTTP do google.com, jedną po drugiej. Problem polega na zażądaniu nr 5 i wstrzymaniu na około 3 minuty, a następnie kontynuowaniu przetwarzania żądań 6 - 10, a następnie wstrzymaniu na kolejne 3 minuty, a następnie żądaniu 11-15, wstrzymaniu i tak dalej. Edit:Próbuję zmienić www.google.com localhost, bardzo podstawowa aplikacja node.js prowadzę maszynę, która zwraca „Hello World”, ja nadal się 3 minuty pauzy.

Teraz czytam mogę zwiększyć limit puli połączeń:

http.globalAgent.maxSockets = 20; 

Teraz jeśli go uruchomić, to przetwarza żądania 1 - 20, a następnie zatrzymuje się na 3 minuty, a następnie żąda 21 - 40, a następnie zatrzymana , i tak dalej.

Wreszcie, po trochę badań, nauczyłem mogłem wyłączyć połączenie łączenie całości przez ustawienie agent: false w opcjach kupna:

http.request({ host: 'www.google.com', method: 'GET', agent: false }, function(res) { 
    ...snip.... 

... i będzie ona przebiegać przez wszystkich 2000 wniosków dobrze.

Moje pytanie, czy to dobry pomysł, aby to zrobić? Czy istnieje niebezpieczeństwo, że mógłbym otrzymać zbyt wiele połączeń HTTP? I dlaczego zatrzymuje się na 3 minuty, z pewnością, gdybym skończył połączenie, powinien dodać go z powrotem do puli, aby był gotowy na kolejną prośbę, dlaczego więc czeka 3 minuty? Przebacz mojej niewiedzy.

W takim razie jaka jest najlepsza strategia dla aplikacji Node.js, która generuje potencjalnie dużą liczbę żądań HTTP bez blokowania lub awarii?

Używam Node.js w wersji 0.10 na Mac OSX 10.8.2.


Edit: Znalazłem jeśli przekonwertować powyższy kod w pętli for i starają się ustalić kilka połączeń w tym samym czasie, zaczynam się błędy po około 242 połączeń. Błąd jest:

Error was thrown: connect EMFILE 
(libuv) Failed to create kqueue (24) 

... i kod ...

for (var i = 1; i <= 2000; i++) { 
    (function(requestNo) { 
     var request = http.request({ host: 'www.google.com', method: 'GET', agent: false }, function(res) { 
      console.log('Completed ' + requestNo); 
     }); 

     request.on('error', function(e) { 
      console.log(e.name + ' was thrown: ' + e.message); 
     }); 

     request.end(); 
    })(i); 
} 

Nie wiem, czy mocno załadowana aplikacja node.js może kiedykolwiek osiągnąć, że wiele jednoczesnych połączeń.

+1

Wyczerpuje się deskryptor plików, który na OSX jest domyślnie ograniczony do raczej niskiego 256. Możesz zwiększyć tę liczbę, używając 'ulimit -n 2048', która umożliwiłaby kolejny proces Node uruchamiany z tej samej powłoki, aby otworzyć te 2000 połączeń z Google ** w tym samym czasie **, ale to naprawdę nie jest to, o czym myślę . Nie jestem pewien, skąd pochodzą 3 minuty, brzmi jak dławienie w puli połączeń (czy może Google dusi cię?). – robertklep

+0

Dzięki za informacje na temat deskryptorów plików OSX, ma to większy sens. Domyślam się, że nie byłoby problemu na stronie na żywo działającej pod Linuksem. Ale co do czasu oczekiwania na 3 minuty, dostaję to, jeśli trafię na lokalnie działającą aplikację internetową Node.js na moim komputerze. –

+1

Czytanie [this] (http://nodejs.org/api/http.html#http_class_http_agent), Zastanawiam się, czy 3-minutowy limit czasu jest utrzymywaniem limitu czasu dla serwerów Google (chociaż jeśli rozumiem dokumenty poprawnie, jako dopóki będziesz żądał, nie powinien czekać, aż te alimenty wygaśnie przed uruchomieniem nowego żądania ...) – robertklep

Odpowiedz

18

Musisz skonsumować odpowiedź.

Pamiętaj, że w v0.10 wylądowaliśmy strumieniami2. Oznacza to, że zdarzenia nie następują, dopóki ich nie rozpoczniesz. Tak, można robić rzeczy tak:

http.createServer(function(req, res) { 
    // this does some I/O, async 
    // in 0.8, you'd lose data chunks, or even the 'end' event! 
    lookUpSessionInDb(req, function(er, session) { 
    if (er) { 
     res.statusCode = 500; 
     res.end("oopsie"); 
    } else { 
     // no data lost 
     req.on('data', handleUpload); 
     // end event didn't fire while we were looking it up 
     req.on('end', function() { 
     res.end('ok, got your stuff'); 
     }); 
    } 
    }); 
}); 

Jednak druga strona strumieni, które nie utracić dane, jeśli nie czytasz go, jest to, że faktycznie nie utracić dane, jeśli nie czytasz tego! Oznacza to, że zaczynają się na pauzę i trzeba je przeczytać, aby uzyskać cokolwiek.

To, co dzieje się w teście, polega na tym, że robisz kilka żądań, a nie zużywasz odpowiedzi, a ostatecznie gniazdo zostaje zabite przez Google, ponieważ nic się nie dzieje i zakłada, że ​​umarłeś .

Istnieją pewne przypadki, w których jest to niemożliwe zużywają przychodzącej wiadomości, to znaczy, jeśli nie dodać obsługi response zdarzeń na prośby, lub gdzie całkowicie pisać i zakończyć wiadomość response na serwerze bez kiedykolwiek czytając prośbę. W takich przypadkach po prostu wyrzucamy dane do śmieci.

Jeśli jednak słuchasz wydarzenia 'response', to Ty odpowiadasz za obsługę obiektu. Dodaj numer response.resume() w swoim pierwszym przykładzie, a zobaczysz, że proces przebiega w rozsądnym tempie.

+2

Słodko, dzięki za to! Tak, "response.resume()" działa. I, jak mówisz, po prostu zużywa się odpowiedź z "response.on (" data ", function() {})" również działa. Również wywoływanie "this.destroy()" w wywołaniu zwrotnym również działa. –

+0

Chciałbym również dodać, że nie jest to bardzo jasne w dokumentacji http://nodejs.org/api/http.html#http_http_request_options_callback - ale prawdopodobnie zrozumiałe, jeśli jest to nowe zachowanie kupione przez stream2, a 0.10 dopiero co zostało wydany. –

+0

Gdzie w tym rozwiązaniu kodu występuje http.request? Jak wygląda pełny kod w innych słowach? – TetraDev