2011-08-17 9 views
6

Ho il seguente server HTTP semplice utilizzando Node.js:Perché questo blocco JavaScript in Node.js?

var http = require('http'); 

var server = http.createServer(function(req, res) { 
    var counter = 0; 

    for(var i = 1; i <= 30; i++) { 
     http.get({ host: "www.google.com" }, function(r) { 
      counter++; 
      res.write("Response " + counter + ": " + r.statusCode + "\n"); 
      if(counter == 30) res.end();                                 
     }); 
    } 
}); 

server.listen(8000); 

Quando mi rannicchio nel mio host locale sulla porta 8000, io capisco il risultato atteso di:

Response 1: 200 
Response 2: 200 
Response 3: 200 
... 
Response 30: 200 

Ma quando ho prova ad arricciarmi da un altro terminale mentre il primo processo è in esecuzione, vedo la console bloccata e aspetto che il primo processo finisca completamente prima che inizi a ricevere lo stesso output.

La mia comprensione è che poiché questo è il codice asincrono che utilizza i callback, tale nodo può gestire più richieste sincronizzate elaborandole sul tick successivo del ciclo degli eventi. E infatti ho persino visto un video di Ryan Dahl fare qualcosa di simile con un esempio di ciao mondo. Cosa c'è nel mio codice che sta facendo bloccare il server?

+0

Vale anche la pena notare che, oltre a poter configurare 'http.globalAgent.maxSockets' come un numero intero più grande (come specificato in varie risposte), è anche possibile impostarlo su' Infinito' per rimuovere del tutto i limiti. – skeggse

risposta

8

Il tuo problema non ha nulla a che fare con il blocco delle chiamate; questo ha a che fare con il fatto che si è in grado di aprire un certo numero di connessioni alla volta in un singolo host. Una volta raggiunto il numero massimo di connessioni aperte, le altre chiamate asincrone a http.get devono attendere fino a quando il numero di connessioni aperte cade di nuovo, cosa che accade quando le altre richieste sono complete e le loro richiamate vengono attivate. Dal momento che stai creando nuove richieste più velocemente di quanto non riescano, ottieni i tuoi risultati apparentemente bloccanti.

Ecco una versione modificata del programma che ho creato per testarlo. (Nota che c'è un modo più semplice per risolvere il tuo problema, come indicato da mtomis - più su questo sotto.) Ho aggiunto alcuni logging console.log, quindi è più facile capire quale ordine sono stati elaborati; Rifiuto anche tutte le richieste di altro oltre a /, in modo che le richieste favicon.ico vengano ignorate. Infine, faccio richieste a molti vari siti web.

var http = require('http'); 

// http://mostpopularwebsites.net/1-50/ 
var sites = [ 
    "www.google.com", "www.facebook.com", "www.youtube.com", 
    "www.yahoo.com", "www.blogspot.com", "www.baidu.com", "www.live.com", 
    "www.wikipedia.org", "www.twitter.com", "www.qq.com", "www.msn.com", 
    "www.yahoo.co.jp", "www.sina.com.cn", "www.google.co.in", "www.taobao.com", 
    "www.amazon.com", "www.linkedin.com", "www.google.com.hk", 
    "www.wordpress.com", "www.google.de", "www.bing.com", "www.google.co.uk", 
    "www.yandex.ru", "www.ebay.com", "www.google.co.jp", "www.microsoft.com", 
    "www.google.fr", "www.163.com", "www.google.com.br", 
    "www.googleusercontent.com", "www.flickr.com" 
]; 

var server = http.createServer(function(req, res) { 
    console.log("Got a connection."); 
    if(req.url != "/") { 
    console.log("But returning because the path was not '/'"); 
    res.end(); 
    return; 
    } 

    var counter = 0; 

    for(var i = 1; i <= 30; i++) { 
    http.get({ host: sites[i] }, function(index, host, r) { 
     counter++; 
     console.log("Response " + counter + " from # " + index + " (" + host + ")"); 
     res.write("Response " + counter + " from # " + index + " (" + host + ")\n"); 
     if(counter == 30) res.end(); 
    }.bind(this, i, sites[i])); 
    } 
    console.log("Done with for loop."); 
}); 

server.listen(8000); 

ho corse questo programma e molto rapidamente visitato la pagina in due diversi browser (ho anche Arrossii la cache DNS, come il test è stato eseguito troppo rapidamente per ottenere una buona uscita in altro modo). Ecco l'output:

Got a connection. 
Done with for loop. 
Response 1 from # 8 (www.twitter.com) 
Response 2 from # 1 (www.facebook.com) 
Response 3 from # 12 (www.sina.com.cn) 
Response 4 from # 4 (www.blogspot.com) 
Response 5 from # 13 (www.google.co.in) 
Response 6 from # 19 (www.google.de) 
Response 7 from # 26 (www.google.fr) 
Response 8 from # 28 (www.google.com.br) 
Response 9 from # 17 (www.google.com.hk) 
Response 10 from # 6 (www.live.com) 
Response 11 from # 20 (www.bing.com) 
Response 12 from # 29 (www.googleusercontent.com) 
Got a connection. 
Done with for loop. 
Response 13 from # 10 (www.msn.com) 
Response 14 from # 2 (www.youtube.com) 
Response 15 from # 18 (www.wordpress.com) 
Response 16 from # 16 (www.linkedin.com) 
Response 17 from # 7 (www.wikipedia.org) 
Response 18 from # 3 (www.yahoo.com) 
Response 19 from # 15 (www.amazon.com) 
Response 1 from # 6 (www.live.com) 
Response 2 from # 1 (www.facebook.com) 
Response 3 from # 8 (www.twitter.com) 
Response 4 from # 4 (www.blogspot.com) 
Response 20 from # 11 (www.yahoo.co.jp) 
Response 21 from # 9 (www.qq.com) 
Response 5 from # 2 (www.youtube.com) 
Response 6 from # 13 (www.google.co.in) 
Response 7 from # 10 (www.msn.com) 
Response 8 from # 24 (www.google.co.jp) 
Response 9 from # 17 (www.google.com.hk) 
Response 10 from # 18 (www.wordpress.com) 
Response 11 from # 16 (www.linkedin.com) 
Response 12 from # 3 (www.yahoo.com) 
Response 13 from # 12 (www.sina.com.cn) 
Response 14 from # 11 (www.yahoo.co.jp) 
Response 15 from # 7 (www.wikipedia.org) 
Response 16 from # 15 (www.amazon.com) 
Response 17 from # 9 (www.qq.com) 
Response 22 from # 5 (www.baidu.com) 
Response 23 from # 27 (www.163.com) 
Response 24 from # 14 (www.taobao.com) 
Response 18 from # 5 (www.baidu.com) 
Response 19 from # 14 (www.taobao.com) 
Response 25 from # 24 (www.google.co.jp) 
Response 26 from # 30 (www.flickr.com) 
Response 20 from # 29 (www.googleusercontent.com) 
Response 21 from # 22 (www.yandex.ru) 
Response 27 from # 23 (www.ebay.com) 
Response 22 from # 19 (www.google.de) 
Response 23 from # 21 (www.google.co.uk) 
Response 24 from # 28 (www.google.com.br) 
Response 25 from # 25 (www.microsoft.com) 
Response 26 from # 20 (www.bing.com) 
Response 27 from # 30 (www.flickr.com) 
Response 28 from # 22 (www.yandex.ru) 
Response 28 from # 27 (www.163.com) 
Response 29 from # 25 (www.microsoft.com) 
Response 29 from # 26 (www.google.fr) 
Response 30 from # 21 (www.google.co.uk) 
Response 30 from # 23 (www.ebay.com) 
Got a connection. 
But returning because the path was not '/' 

Come si può vedere, oltre al periodo di tempo che mi ha portato a colpire Alt+Tab Enter, i callback sono completamente mescolati - asincrono, non-blocking I/O al suo culmine.

[Edit]

Come mtomis menzionati, il numero di connessioni massime che si possono avere al verde a host è configurabile tramite il globale http.globalAgent.maxSockets. Basta impostarlo sul numero di connessioni simultanee che si desidera gestire per host e il problema osservato scompare.

+0

Puoi fare riferimento perché "ogni chiamata a http.get non può tornare e attivare la sua richiamata fino a quando la chiamata precedente non ritorna". Presupponevo che generasse richieste HTTP GET parallele – Raynos

+0

@Brandon Tilley, dovresti scrivere di più sugli agenti e su come eluderli. – thejh

0

Beh, in realtà non stai generando le richieste che penso, in qualcosa che può essere richiamato. Hai solo un gestore di eventi e sta eseguendo un ciclo tutto in fila.

Riesci a trovare dove Ryan Dahl ha pronunciato quel discorso?

5

Nodo.js ha un limite per le connessioni client per host (per impostazione predefinita 5 connessioni per host), come documentato qui: http://nodejs.org/docs/v0.5.4/api/http.html#agent.maxSockets

Il motivo per cui il secondo processo di arricciatura si blocca fino al completamento del primo è perché il primo processo accoda 30 richieste, 5 di cui può essere gestito allo stesso tempo, quindi le successive 30 richieste del secondo processo non possono essere gestite fino a quando le prime non sono complete. Nell'esempio se si imposta http.globalAgent.maxSockets = 60;, le chiamate verranno gestite contemporaneamente.

+0

+1, in qualche modo ho saltato la documentazione di 'http.globalAgent'. –

Problemi correlati