2011-08-17 30 views
6

Tengo el siguiente servidor http simple usando Node.js:¿Por qué este bloque de javascript en 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); 

Cuando me hundo en mi máquina local en el puerto 8000, que hago obtener el resultado esperado de:

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

Pero cuando Intentar curl desde otro terminal mientras se está ejecutando el primer proceso, veo que la consola se cuelga y espero a que el primer proceso termine por completo antes de que comience a recibir el mismo resultado.

Según entendí, dado que se trata de un código asíncrono que usa devoluciones de llamada, ese nodo podría manejar múltiples solicitudes en sincronización procesándolas en el siguiente tic del bucle de evento. Y, de hecho, incluso vi un video de Ryan Dahl haciendo algo similar con un ejemplo de Hello World. ¿Qué hay en mi código que está bloqueando el servidor?

+0

También vale la pena señalar que, además de ser capaz de configurar 'httpGlobalAgent.maxSockets' como un entero más grande (como se especifica en varias respuestas), también puede establecerlo en' Infinity' para eliminar los límites por completo. – skeggse

Respuesta

8

Su problema no tiene nada que ver con el bloqueo de llamadas; esto tiene que ver con el hecho de que solo puede abrir una determinada cantidad de conexiones a la vez a un solo host. Una vez que alcanza la cantidad máxima de conexiones abiertas, las otras llamadas asíncronas al http.get tienen que esperar hasta que el número de conexiones abiertas vuelva a caer, lo que sucede cuando las demás solicitudes se completan y se activan sus devoluciones de llamada. Ya que estás creando nuevas solicitudes más rápido de lo que agotan, obtienes los resultados aparentemente bloqueantes.

Aquí hay una versión modificada de su programa que creé para probar esto. (Tenga en cuenta que hay una manera más fácil de resolver su problema, como lo indica mtomis - más sobre esto a continuación.) Agregué un poco de registro console.log, por lo que es más fácil saber en qué orden se procesaron las cosas; También rechazo todas las solicitudes para cualquier cosa que no sea /, por lo que se ignoran las solicitudes favicon.ico. Finalmente, realizo solicitudes a muchos sitios 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); 

que corrió este programa y muy rápidamente visitado la página en dos navegadores diferentes (también Enrojecí mi caché DNS, como la prueba estaba corriendo demasiado rápido para obtener un buen resultado de lo contrario). Aquí está la salida:

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 '/' 

Como se puede ver, que no sea el período de tiempo que me llevó a golpear Alt+Tab Enter, las devoluciones de llamada están completamente entremezclados - asíncrona, sin bloqueo de E/S en su máxima expresión.

[Editar]

Como mtomis mencionados, el número máximo de conexiones que puede tener abiertos por host es configurable a través del mundial http.globalAgent.maxSockets. Simplemente configure esto como el número de conexiones simultáneas que desea controlar por host, y el problema que observó desaparece.

+0

¿Puedes consultar por qué "cada llamada a http.get no puede regresar y activar su devolución de llamada hasta que vuelva la llamada anterior". Supuse que generaba solicitudes HTTP GET paralelas – Raynos

+0

@Brandon Tilley, debería escribir más sobre agentes y cómo eludirlos. – thejh

0

Bueno, en realidad no está generando las solicitudes, creo, en algo que se puede devolver. Solo tiene un controlador de eventos y ejecuta un ciclo todo en una fila.

¿Puedes encontrar dónde Ryan Dahl dio esa charla?

5

Node.js tiene un límite para las conexiones de cliente por host (de manera predeterminada 5 conexiones por host), como se documenta aquí: http://nodejs.org/docs/v0.5.4/api/http.html#agent.maxSockets

El motivo por el que su segundo proceso curl se cuelga hasta que el primero finaliza es porque el primer proceso hace 30 solicitudes, 5 de los cuales se pueden manejar al mismo tiempo, por lo tanto, las siguientes 30 solicitudes del segundo proceso no se pueden manejar hasta que se completen las primeras. En su ejemplo si establece http.globalAgent.maxSockets = 60;, las llamadas se manejarán al mismo tiempo.

+0

+1, de alguna manera omití la documentación de 'http.globalAgent'. –

Cuestiones relacionadas