为什么这个JavaScript在Node.js中阻塞?

我有以下简单的http服务器使用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); 

当我卷入8000端口的本地主机时,我确实得到了预期的结果:

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

但是当我在第一个进程运行的时候试图从另一个terminal进入,我看到控制台挂起,等待第一个进程完全完成,然后才开始接收相同的输出。

我的理解是,因为这是使用callback的asynchronous代码,所以节点可以通过在事件循环的下一个记号上处理它们来同步处理多个请求。 事实上,我甚至观看了一个Ryan Dahl的video,他正在做一个类似Hello World的例子。 我的代码是什么使服务器块?

你的问题与阻止呼叫没有任何关系; 这与您只能一次打开一定数量的连接到单个主机的事实有关。 一旦你达到了打开连接的最大数量,对http.get的其他asynchronous调用就必须等待,直到打开连接的数量再次下降,这发生在其他请求完成并且它们的callback被触发时。 既然你创build新的请求比他们更快,你会得到看似阻塞的结果。

这里是我为了testing这个而创build的程序的一个修改版本。 (请注意,有一个更简单的方法来解决你的问题,正如mtomis所指出的那样 – 下面的更多内容。)我添加了一些console.log日志logging,所以更容易判断正在处理的是什么命令。 我也拒绝除/以外的所有请求,以便favicon.ico请求被忽略。 最后,我向很多不同的网站提出请求。

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

我运行这个程序,很快就访问了两个不同浏览器的页面(我也冲洗了我的DNScaching,因为testing运行得太快,以至于无法获得良好的输出)。 这是输出:

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

正如您所看到的,除了我按Alt+Tab Enter ,callback函数完全混合在一起 – asynchronous的,非阻塞的I / O是最好的。

[编辑]

正如mtomis提到的,每个主机可以打开的最大连接数可以通过全局http.globalAgent.maxSocketsconfiguration。 只需将此值设置为您希望能够处理的并发连接数,并且您观察到的问题将消失。

Node.js对每个主机的客户端连接数有限制(每个主机默认5个连接),如下所示: http : //nodejs.org/docs/v0.5.4/api/http.html#agent.maxSockets

第二个curl进程挂起直到第一个进程结束的原因是,第一个进程队列有30个请求,其中5个可以同时处理,因此第二个进程的下一个30个请求不能被处理,直到第一个进程完成。 在你的例子中,如果你设置http.globalAgent.maxSockets = 60; 那么这些调用将被同时处理。

那么你实际上并没有产生我认为的要求,成为可以callback的东西。 你只有一个事件处理程序,它连续运行一个循环。

你能findRyan Dahl在那里说的吗?