代码之家  ›  专栏  ›  技术社区  ›  Sunday Ironfoot

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

  •  10
  • Sunday Ironfoot  · 技术社区  · 12 年前

    考虑以下简单的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();
    

    它一个接一个地向谷歌发出2000个HTTP请求。问题是,它收到5号请求并暂停约3分钟,然后继续处理6-10号请求,然后再暂停3分钟,再请求11-15,暂停,依此类推。 编辑: 我试着将www.google.com更改为localhost,这是一个运行我的机器的非常基本的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 回复  |  直到 12 年前
        1
  •  19
  •   isaacs    12 年前

    您必须使用响应。

    请记住,在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.resume() 在您的第一个示例中,您将看到它以合理的速度进行处理。