【问题标题】:Node js - http.request() problems with connection poolingNode js - 连接池的 http.request() 问题
【发布时间】:2013-03-10 03:09:53
【问题描述】:

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

它一个接一个地向 google.com 发出 2000 个 HTTP 请求。问题是它到达请求 5 并暂停大约 3 分钟,然后继续处理请求 6 - 10,然后再暂停 3 分钟,然后请求 11 - 15,暂停,依此类推。 编辑: 我尝试将 www.google.com 更改为 localhost,这是一个非常基本的 Node.js 应用程序,运行我的机器并返回“Hello world”,但我仍然会暂停 3 分钟。 em>

现在我读到我可以增加连接池限制:

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 应用程序是否可以同时连接那么多。

【问题讨论】:

  • 您的文件描述符用完了,在 OSX 上,默认情况下限制为相当低的 256。您可以使用 ulimit -n 2048 增加该数字,这将允许从同一 shell 运行的后续节点进程同时打开与 Google 的 2000 个连接,但这并不是您真正想要的我认为.我不确定 3 分钟是从哪里来的,这听起来像是连接池中的一个限制(或者谷歌可能在限制你?)。
  • 感谢有关 OSX 文件描述符的信息,这更有意义。我想在 Linux 上运行的实时站点上不会有问题。但至于 3 分钟的等待时间,如果我在我的机器上点击本地运行的 Node.js Web 应用程序,我就会明白。
  • 阅读this,我想知道3分钟超时是否是Google服务器的保持活动超时(尽管如果我正确理解文档,只要您继续请求,它不应该在开始新请求之前等待这些保持活动到期...)
  • robertklep - 请参阅上面的编辑,但我尝试将 www.google.com 更改为 localhost,这是一个非常基本的 Node.js 应用程序,运行我的机器并返回“Hello world”,我仍然得到 3 分钟的暂停。
  • 我的猜测是3分钟来自内存分配

标签: node.js request connection-pooling


【解决方案1】:

您必须使用响应。

请记住,在 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(),您会看到它以合理的速度处理。

【讨论】:

  • 太好了,谢谢!是的,“response.resume()”有效。而且,正如您所说,仅使用“response.on('data',function(){})”来使用响应也可以。此外,在回调中调用“this.destroy()”似乎也可以。
  • 我还要补充一点,这在文档 nodejs.org/api/http.html#http_http_request_options_callback 中并没有说得很清楚 - 但如果这是由 stream2 购买的新行为并且 0.10 刚刚发布,这可能是可以理解的。
  • http.request 在这个代码解决方案中发生在哪里?换句话说,完整的代码是什么样的?
猜你喜欢
  • 2018-09-18
  • 1970-01-01
  • 1970-01-01
  • 2021-07-13
  • 1970-01-01
  • 2011-10-07
  • 2011-04-01
  • 2012-09-25
  • 2011-04-23
相关资源
最近更新 更多