2011-08-17 53 views
6

我如何使用Node.js以下简单的HTTP服务器:为什么在JavaScript中阻止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 

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

我的理解是,因为这是使用回调的异步代码,所以节点可以通过在事件循环的下一个记号上处理它们来同步处理多个请求。事实上,我甚至观看了Ryan Dahl的一个录像,以一个类似Hello World的例子来做类似的事情。我的代码中有什么让服务器阻塞?

+0

还值得注意的是,除了能够将'http.globalAgent.maxSockets'配置为一个更大的整数(在不同的答案中指定)之外,您还可以将它设置为'Infinity'来完全删除限制。 – skeggse

回答

8

您的问题与阻止呼叫没有任何关系;这与您只能一次打开一定数量的连接到单个主机的事实有关。一旦您达到最大打开连接数,其他异步调用http.get必须等到打开的连接数再次下降,这发生在其他请求完成并且它们的回调被触发时。由于您创建新请求的速度比他们耗尽的速度快,因此您会看到阻止的结果。

这里是我为了测试这个而创建的程序的一个修改版本。 (请注意,有一种更简单的方法可以解决您的问题,正如mtomis所示 - 下文详细介绍)。我添加了一些console.log日志记录,因此更容易说明正在处理的内容的顺序;我还拒绝除/以外的所有请求,以便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); 

我跑这个程序,并很快在两个不同的浏览器访问该页面(我也刷新我的DNS缓存,作为供试品正在运行太快,否则得到很好的输出)。下面是输出:

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期间等,回调是完全混合在一起 - 在其最好的异步非阻塞I/O。

[编辑]

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

+0

您可以引用为什么“每次调用http.get都无法返回并触发其回调,直到前一个调用返回”。我认为它产生了并行HTTP GET请求 – Raynos

+0

@Brandon Tilley,你应该写更多关于代理和如何规避它们。 – thejh

0

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

你能找到Ryan Dahl在那里发表的言论吗?

5

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

第二个curl进程挂起直到第一个curl进程结束的原因是第一个进程队列有30个请求,5其中可以同时处理,因此第二个过程的下一个30个请求在第一个过程完成之前不能被处理。在您的 示例中,如果您设置了http.globalAgent.maxSockets = 60;,则会同时处理这些呼叫。

+0

+1,不知何故,我跳过了'http.globalAgent'的文档。 –

相关问题