2013-03-20 16 views
10

Si consideri la seguente applicazione semplice Node.js:Nodo js - http.request() problemi con il pool di connessioni

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(); 

Rende 2000 le richieste HTTP a google.com, uno dopo l'altro. Il problema è che si ottiene la richiesta n. 5 e si interrompe per circa 3 minuti, quindi continua l'elaborazione delle richieste da 6 a 10, quindi si interrompe per altri 3 minuti, quindi si richiedono 11-15, pause e così via. Modifica:Ho provato a cambiare www.google.com in localhost, un'app Node.js estremamente semplice che esegue la mia macchina che restituisce "Hello world", ho ancora la pausa di 3 minuti.

Ora ho letto posso aumentare il limite di pool di connessioni:

http.globalAgent.maxSockets = 20; 

Ora, se l'eseguo, esso elabora le richieste 1-20, poi si ferma per 3 minuti, quindi richiede 21 - 40, quindi le pause , e così via.

Infine, dopo un po 'di ricerca, ho imparato ho potuto disattivare il pool di connessioni interamente impostando agent: false nelle opzioni di richiesta:

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

... e sarà gestita attraverso tutti 2000 le richieste più che bene.

La mia domanda, è una buona idea farlo? C'è il pericolo che io possa finire con troppe connessioni HTTP? E perché si ferma per 3 minuti, sicuramente se ho finito con la connessione dovrebbe aggiungerlo direttamente in piscina pronto per la prossima richiesta da usare, quindi perché aspetta 3 minuti? Perdona la mia ignoranza.

In caso contrario, qual è la strategia migliore per un'app Node.js che effettua un numero potenzialmente elevato di richieste HTTP, senza blocco o arresto anomalo?

Sto eseguendo Node.js versione 0.10 su Mac OS X 10.8.2.


Edit: ho trovato se converto il codice sopra in un ciclo for e cercare di stabilire una serie di connessioni, allo stesso tempo, ho iniziare a ricevere gli errori dopo circa 242 connessioni. L'errore è:

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

... e il codice ...

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); 
} 

non so se un Node.js app pesantemente caricato potrebbe mai raggiungere quel numero di connessioni simultanee.

+1

Si stanno esaurendo i descrittori di file, che su OSX sono limitati a un 256 piuttosto basso per impostazione predefinita. Puoi aumentare quel numero usando 'ulimit -n 2048', che consentirebbe un successivo processo di nodo eseguito dalla stessa shell per aprire quelle 2000 connessioni a Google ** allo stesso tempo **, ma non è proprio quello che vuoi . Non sono sicuro di dove arrivino i 3 minuti, suona come una strozzatura nel pool di connessione (o forse Google ti sta soffocando?). – robertklep

+0

Grazie per le informazioni sui descrittori di file OSX, ha un po 'più senso. Immagino che non sarebbe un problema su un sito live in esecuzione su Linux. Ma per quanto riguarda i 3 minuti di attesa, ho capito che se avessi colpito un'applicazione web Node.js in esecuzione sul mio computer. –

+1

Leggendo [questo] (http://nodejs.org/api/http.html#http_class_http_agent), mi chiedo se il timeout di 3 minuti sia il timeout keep-alive per i server di Google (anche se comprendo correttamente i documenti, come a mano a mano che continui a richiedere, non dovrebbe aspettare la scadenza dei keep-alive prima di iniziare una nuova richiesta ...) – robertklep

risposta

18

Devi consumare la risposta.

Ricordate, in v0.10, abbiamo atterrato stream2. Ciò significa che gli eventi data non si verificano finché non inizi a cercarli. Così, si può fare cose come questa:

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'); 
     }); 
    } 
    }); 
}); 

Tuttavia, il rovescio della medaglia di corsi d'acqua che non perdere i dati quando non state leggendo, è che essi realtà non perdere i dati se non lo stai leggendo! Cioè, iniziano in pausa, e devi leggerli per ottenere qualcosa.

Quindi, quello che sta succedendo nel tuo test è che stai facendo un sacco di richieste e non consumare le risposte, e poi alla fine il socket viene ucciso da google perché non sta succedendo nulla, e si presume che tu sia morto .

Ci sono alcuni casi in cui è impossibile di consumare il messaggio in arrivo: che è, se non si aggiunge un gestore response evento su un richieste, o dove si completamente scrivere e completato il messaggio response su un server senza sempre a leggere la richiesta. In questi casi, semplicemente scarichiamo i dati nella spazzatura per te.

Tuttavia, se si sta ascoltando l'evento 'response', è responsabilità dell'utente gestire l'oggetto. Aggiungi un response.resume() nel tuo primo esempio e vedrai che procede fino a un ritmo ragionevole.

+2

Dolce, grazie per quello! Sì, "response.resume()" funziona. E, come dici tu, consumare solo la risposta con "response.on ('data', function() {})" funziona anche. Inoltre, anche la chiamata "this.destroy()" nel callback sembra funzionare bene. –

+0

Vorrei anche aggiungere che questo non è reso molto chiaro nella documentazione http://nodejs.org/api/http.html#http_http_request_options_callback - ma probabilmente comprensibile se si tratta di un nuovo comportamento acquistato da streams2 e 0.10 è appena stato rilasciato. –

+0

Dove si verifica l'http.request in questa soluzione di codice? In altre parole, come appare il codice completo? – TetraDev

Problemi correlati