问题 使用Node.js http.get时,套接字似乎没有关闭


OS X 10.8.3

节点0.10.0

我正在使用'http'模块来请求Facebook图形API。

以下是我传递给'http.get'的选项:

var options = {host: 'graph.facebook.com',
               port: 80,
               path: '/' + fb_id + '/picture'};  //fb_id is a Facebook user identifier

我的代码如下所示:

http.get(options,
  function(res) {
    ...some stuff...
    DONE(RESULT);  //DONE is a callback function
  }).on('error', function(e) {
       ...some error handling...
});

我观察到的是我只能执行与http.globalAgent.maxSockets值相同的请求。一旦我达到了那么多请求,下一次对http.get的调用永远(显然)连接。我已经确认我没有收到错误的请求。

好像响应进来后套接字没有关闭。

作为响应处理程序的一部分,我需要做些什么才能确保套接字关闭?

由于默认的keepalive行为,这些套接字是否未关闭?

我该如何进行调试呢?


3884
2018-04-09 18:36


起源



答案:


尝试设置 agent: false 在选项中。默认行为确实是为了保持HTTP保持连接的连接:

var options = {host: 'graph.facebook.com',
               port: 80,
               path: '/' + fb_id + '/picture',
               agent: false};

7
2018-04-09 18:50



我在文档中看到了这一点,但发现它令人困惑:“如果没有挂起的HTTP请求等待套接字变为空闲,则套接字将被关闭。这意味着节点的池在加载时具有保持活动的优点,但仍然不需要开发人员使用keep-alive手动关闭HTTP客户端。“我不明白为什么汇集的,已打开的连接不用于后续请求。我会尝试代理:假,并期待从中获得好处。 - Wes Gamble
我觉得 http 模块不会对池中没有套接字的任何请求进行排队。如果 maxSockets到达并且所有这些请求仍然处于挂起状态,任何后续请求都无法从池中获得空闲套接字并且将被丢弃。 - robertklep
我意识到我的应用程序架构导致了我的问题,因为我在每个请求之前重新要求'http'模块,这意味着每个http引用都是独立的。我试图改变这一点,以便在'get'调用中共享http模块引用,但我看到相同的行为(第一个globalAgent.maxSockets请求很好,其余的因为缺少套接字而挂起)。这引出了一个问题 - 如何利用这种默认的keepalive行为? - Wes Gamble
罗伯特,你的评论是有道理的,但我知道这些回复是成功的,我认为这意味着相关的请求仍未处理。我将整个“keepalive”默认行为表示“http模块将在同一个套接字上允许多个get请求而不关闭/重新打开它”。那是对的吗? - Wes Gamble
@GeniaS。节点 流程 是独立的,它们之间没有共享套接字池或任何东西。从Node 0.12开始, maxSockets 被设定为 Infinity,显然以前(0.12之前)的限制(我相信5)被认为是限制性太强。在某些时候,有数千个并发连接到远程HTTP服务器,你 将 遇到问题,但限制并发请求的数量可以更好地在代码中显式完成,而不是通过一些硬编码的默认值。 - robertklep


答案:


尝试设置 agent: false 在选项中。默认行为确实是为了保持HTTP保持连接的连接:

var options = {host: 'graph.facebook.com',
               port: 80,
               path: '/' + fb_id + '/picture',
               agent: false};

7
2018-04-09 18:50



我在文档中看到了这一点,但发现它令人困惑:“如果没有挂起的HTTP请求等待套接字变为空闲,则套接字将被关闭。这意味着节点的池在加载时具有保持活动的优点,但仍然不需要开发人员使用keep-alive手动关闭HTTP客户端。“我不明白为什么汇集的,已打开的连接不用于后续请求。我会尝试代理:假,并期待从中获得好处。 - Wes Gamble
我觉得 http 模块不会对池中没有套接字的任何请求进行排队。如果 maxSockets到达并且所有这些请求仍然处于挂起状态,任何后续请求都无法从池中获得空闲套接字并且将被丢弃。 - robertklep
我意识到我的应用程序架构导致了我的问题,因为我在每个请求之前重新要求'http'模块,这意味着每个http引用都是独立的。我试图改变这一点,以便在'get'调用中共享http模块引用,但我看到相同的行为(第一个globalAgent.maxSockets请求很好,其余的因为缺少套接字而挂起)。这引出了一个问题 - 如何利用这种默认的keepalive行为? - Wes Gamble
罗伯特,你的评论是有道理的,但我知道这些回复是成功的,我认为这意味着相关的请求仍未处理。我将整个“keepalive”默认行为表示“http模块将在同一个套接字上允许多个get请求而不关闭/重新打开它”。那是对的吗? - Wes Gamble
@GeniaS。节点 流程 是独立的,它们之间没有共享套接字池或任何东西。从Node 0.12开始, maxSockets 被设定为 Infinity,显然以前(0.12之前)的限制(我相信5)被认为是限制性太强。在某些时候,有数千个并发连接到远程HTTP服务器,你 将 遇到问题,但限制并发请求的数量可以更好地在代码中显式完成,而不是通过一些硬编码的默认值。 - robertklep


Node的http模块声明代理默认为全局代理: http://nodejs.org/api/http.html#http_http_globalagent,这意味着无论发起请求的模块是什么,都会共享keep-alive。

BTW,回应Wes在2013年4月9日20:47发表的评论:加载节点模块的次数无关紧要,它只会加载一次并由所有模块共享。

您遇到的是游泳池耗尽问题。避免它的最简单方法是使用新代理(http://nodejs.org/api/http.html#http_class_http_agent)使用您想要的maxSockets。请记住,如果将模块放在该模块的导出中,则可以在模块之间共享您创建的代理(节点中的模块是有状态的!!!)。


2
2018-03-24 15:49





我经历了相同的行为,除了我的连接最终在超时期后重复使用。检查连接是否在一段时间(几分钟)后重复使用,并检查响应头是否包含“Connection:keep-alive”。

如果是这种情况,可能的解决方案是使用'Connection:Close'标头而不是keep-alive,那么池化连接可以像通常的设置一样重新使用。我不确定这是否会导致使用facebook端点出现任何性能问题。

var options = {host: 'graph.facebook.com',
               port: 80,
               path: '/' + fb_id + '/picture',
               headers: { 'Connection':'Close' }
};

对我来说使用agent:false没有用,因为我发送了大量请求耗尽了服务器资源。


2
2017-07-01 11:54