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

时间:2011-08-17 03:31:09

标签: javascript node.js nonblocking event-loop

我有以下使用Node.js的简单http服务器:

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的例子。我的代码中有什么使服务器阻塞?

3 个答案:

答案 0 :(得分: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进行配置。只需将其设置为您希望每个主机能够处理的并发连接数,并且您观察到的问题就会消失。

答案 1 :(得分:5)

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

你的第二个curl进程挂起直到第一个curl进程完成的原因是因为第一个进程排队30个请求,其中5个可以同时处理,因此第二个进程的下30个请求无法处理直到第一个是完整的。在你的 例如,如果您设置http.globalAgent.maxSockets = 60;,那么将同时处理呼叫。

答案 2 :(得分:0)

嗯,你实际上并没有将我想到的请求产生到可以回叫的东西中。你只有一个事件处理程序,它正在连续运行一个循环。

你能找到Ryan Dahl发表演讲的地方吗?