2013-03-20 94 views
10

考虑以下简单的Node.js应用:节点JS - http.request()的问题连接池

var http = require('http'); 
http.createServer(function() { }).listen(8124); // Prevent process shutting down 

var requestNo = 1; 
var maxRequests = 2000; 

function requestTest() { 
    http.request({ host: 'www.google.com', method: 'GET' }, function(res) { 
     console.log('Completed ' + (requestNo++)); 

     if (requestNo <= maxRequests) { 
      requestTest(); 
     } 
    }).end(); 
} 

requestTest(); 

这使得2000的HTTP请求到google.com,一前一后。问题是请求5号并暂停约3分钟,然后继续处理请求6-10,然后再暂停3分钟,然后请求11-15,暂停等等。 编辑:我试着将www.google.com更改为本地主机,这是一个运行我的机器的非常基本的Node.js应用程序,它返回“Hello world”,我仍然停留3分钟。

现在我看我可以增加连接池的限制:

http.globalAgent.maxSockets = 20; 

现在,如果我运行它,它处理请求1 - 20,然后暂停3分钟,然后请求21 - 40,然后暂停, 等等。

最后,一些研究之后,我才知道我可以禁用连接池完全由设置在请求选项agent: false

http.request({ host: 'www.google.com', method: 'GET', agent: false }, function(res) { 
    ...snip.... 

...,它会遍历所有2000点的要求就好运行。

我的问题,这是一个好主意吗?是否有可能导致HTTP连接过多的危险?为什么它暂停3分钟,当然如果我已经完成了连接,它应该直接将它添加到游泳池中以备下一次使用请求,那么为什么它会等待3分钟?原谅我的无知。

如果没有锁定或崩溃,那么Node.js应用程序的最佳策略是做出可能的大量HTTP请求的最佳策略是什么?

我在Mac OSX 10.8.2上运行Node.js版本0.10。


编辑:如果我上面的代码转换成一个for循环,并试图在同一时间来建立一串连接的我发现,我开始后约242连接收到错误。错误是:

Error was thrown: connect EMFILE 
(libuv) Failed to create kqueue (24) 

...和...代码

for (var i = 1; i <= 2000; i++) { 
    (function(requestNo) { 
     var request = http.request({ host: 'www.google.com', method: 'GET', agent: false }, function(res) { 
      console.log('Completed ' + requestNo); 
     }); 

     request.on('error', function(e) { 
      console.log(e.name + ' was thrown: ' + e.message); 
     }); 

     request.end(); 
    })(i); 
} 

我不知道如果重仓的Node.js应用程序都不能达到许多并发连接。

+1

您正在耗尽文件描述符,默认情况下,OSX上的文件描述符限制在相当低的256位。你可以使用'ulimit -n 2048'来增加这个数字,这将允许从同一个shell运行的后续Node进程在同一时间打开与Google **的2000个连接,但这并不是你想要的。我不确定3分钟是从哪里来的,听起来像连接池中的节流器(或者Google可能会阻止你?)。 – robertklep 2013-03-20 21:00:46

+0

感谢有关OSX文件描述符的信息,使其更有意义。我猜这对在Linux上运行的现场网站不会造成问题。但是至于3分钟等待的事情,如果我在我的机器上运行本地运行的Node.js Web应用程序,我会得到这个结果。 – 2013-03-20 21:28:39

+1

阅读[this](http://nodejs.org/api/http.html#http_class_http_agent),我想知道3分钟的超时是否是Google服务器的保持活动超时(尽管如果我正确理解了文档,只要你继续请求,它不应该等待那些保留期限在开始新的请求之前到期......) – robertklep 2013-03-20 22:01:49

回答

18

您必须消耗响应。

请记住,在v0.10中,我们着陆了streams2。这意味着data事件直到你开始寻找它们才会发生。所以,你可以做的东西是这样的:

http.createServer(function(req, res) { 
    // this does some I/O, async 
    // in 0.8, you'd lose data chunks, or even the 'end' event! 
    lookUpSessionInDb(req, function(er, session) { 
    if (er) { 
     res.statusCode = 500; 
     res.end("oopsie"); 
    } else { 
     // no data lost 
     req.on('data', handleUpload); 
     // end event didn't fire while we were looking it up 
     req.on('end', function() { 
     res.end('ok, got your stuff'); 
     }); 
    } 
    }); 
}); 

然而,流的另一面,当你不读它不丢失数据,是他们实际上没有如果丢失数据你不是在读它!也就是说,他们开始暂停,你必须阅读他们才能得到任何东西。

所以,你测试中发生的事情是,你正在做一堆请求,并且没有消费响应,然后最终套接字被谷歌杀死,因为什么都没有发生,并且它假定你已经死了。

有些情况下,这是不可能消费收到的消息:那就是,如果你不上一个请求的服务器上,而不添加response事件处理程序,或者你完全写,并完成response消息不断阅读请求。在这些情况下,我们只是将数据转储到垃圾中。

但是,如果您正在聆听'response'事件,则您有责任处理该对象。在第一个示例中添加一个response.resume(),您将看到它以合理的速度进行处理。

+2

甜,谢谢!是的,“response.resume()”起作用。而且,正如你所说的,仅仅使用“response.on('data',function(){})”来响应也是可行的。另外,在回调中调用“this.destroy()”似乎也可以。 – 2013-03-21 09:26:06

+0

我还会补充说,这在文档http://nodejs.org/api/http.html#http_http_request_options_callback中没有说得很清楚 - 但如果这是通过streams2和0.10购买的新行为仅仅是释放。 – 2013-03-21 09:32:42

+0

http.request在这个代码解决方案中发生了什么?换句话说,完整的代码是什么样的? – TetraDev 2015-10-23 15:23:01