¿Por qué este bloque de JavaScript en Node.js?

Tengo el siguiente server http simple usando 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 encrespe en mi host local en el puerto 8000, obtengo el resultado esperado de:

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

Pero cuando bash acurrucarme desde otra terminal mientras se está ejecutando el primer process, veo que la console se cuelga y espero a que el primer process termine por completo antes de que comience a recibir la misma salida.

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 requestes sincronizadas 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 server?

Su problema no tiene nada que ver con el locking 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 asincrónicas a http.get deben esperar hasta que el número de conexiones abiertas vuelva a caer, lo que sucede cuando las demás requestes se completan y se activan sus devoluciones de llamada. Ya que estás creando nuevas requestes más rápido de lo que agotan, obtienes los resultados aparentemente bloqueantes.

Aquí hay una versión modificada de su progtwig que creé para probar esto. (Tenga en count que hay una manera más fácil de resolver su problema, como lo indica mtomis, más información al respecto más adelante). Agregué algunos loggings de console.log , por lo que es más fácil decir en qué order se procesaron las cosas; También rechazo todas las requestes para cualquier cosa que no sea / , para que las requestes de favicon.ico se ignoren. Finalmente, realizo requestes 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); 

Ejecuté este progtwig y visité muy rápidamente la página en dos browseres diferentes (también descargué mi caching de DNS, ya que la testing se estaba ejecutando demasiado rápido para get un buen resultado de lo contrario). Aquí está el resultado:

 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 puede ver, aparte del período de time que tardé en presionar Alt+Tab Enter , las devoluciones de llamada están completamente entremezcladas: E / S asíncrona y sin locking en su máxima expresión.

[Editar]

Como se menciona a mtomis, la cantidad de conexiones máximas que puede tener abiertas por host se puede configurar a través de http.globalAgent.maxSockets globales. Simplemente configure esto como el número de conexiones simultáneas que desea controlar por host, y el problema que observó desaparece.

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

La razón por la cual su segundo process de curvatura se cuelga hasta que se termina el primero es porque el primer process pone en queue 30 requestes, de las cuales 5 pueden manejarse al mismo time, por lo tanto, las siguientes 30 requestes del segundo process no pueden manejarse hasta que las primeras completar. En su ejemplo, si establece http.globalAgent.maxSockets = 60; entonces las llamadas serán manejadas concurrentemente.

Bueno, en realidad no estás generando las requestes, creo, en algo que pueda devolverse. Solo tiene un controller de events y ejecuta un ciclo todo en una fila.

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