我有以下使用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的例子.我的代码中有什么东西可以阻止服务器阻塞?
解决方法
您的问题与阻止呼叫没有任何关系;这与您只能一次打开一定数量的连接到单个主机的事实有关.一旦达到最大打开连接数,对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进行配置.只需将其设置为您希望每个主机能够处理的并发连接数,您观察到的问题就会消失.