节点js - http.request()连接池问题

Sun*_*oot 10 connection-pooling request node.js

考虑以下简单的Node.js应用程序:

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();
Run Code Online (Sandbox Code Playgroud)

它向google.com发出2000个HTTP请求,一个接一个.问题是它要求第5号并暂停约3分钟,然后继续处理请求6-10,然后再暂停3分钟,然后请求11-15,暂停等等.编辑: 我尝试将www.google.com更改为localhost,这是一个运行我的机器的非常基本的Node.js应用程序,返回"Hello world",我仍然可以暂停3分钟.

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

http.globalAgent.maxSockets = 20;
Run Code Online (Sandbox Code Playgroud)

现在,如果我运行它,它处理请求1 - 20,然后暂停3分钟,然后请求21 - 40,然后暂停,依此类推.

最后,经过一些研究,我了解到我可以完全通过设置agent: false请求选项来禁用连接池:

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

......它会完全满足所有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)
Run Code Online (Sandbox Code Playgroud)

......和代码......

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);
}
Run Code Online (Sandbox Code Playgroud)

我不知道一个负载很重的Node.js应用程序是否可以达到那么多同时连接.

isa*_*acs 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');
      });
    }
  });
});
Run Code Online (Sandbox Code Playgroud)

但是,当您不读取数据时,不丢失数据的流的另一面是,如果您不读取数据,它们实际上不会丢失数据! 也就是说,他们开始暂停,你必须阅读它们才能得到任何结果.

所以,你的测试中发生的事情是,你正在制作一堆请求而不是消费响应,然后最终套接字被google杀死,因为没有发生任何事情,并且它假设你已经死了.

在某些情况下,无法使用传入消息:即,如果不在response请求上添加事件处理程序,或者在response服务器上完全编写并完成消息而不读取请求.在这些情况下,我们只是为您丢弃垃圾中的数据.

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

  • 甜蜜,谢谢!是的,"response.resume()"有效.而且,正如你所说,只是使用"response.on('data',function(){})消费响应"也可以.另外,在回调中调用"this.destroy()"似乎也可以. (2认同)