【问题标题】:Node.js server GET to separate API failing after a few hours of use使用几个小时后,Node.js 服务器 GET 到分离 API 失败
【发布时间】:2012-05-02 11:16:46
【问题描述】:

在我的节点站点中,我调用了一个使用标准 http get 构建的 RESTful API 服务。在成功进行了几个小时的通信后,我发现请求停止发送,它只是等待并最终超时。

被调用的 API 仍然可以很好地接收来自其他地方的请求,但是当从站点发送请求时,它并没有到达 API。

我尝试过使用 stream.pipe、util.pump 并将文件写入文件系统。

我使用的是节点 0.6.15。我的站点和被调用的服务位于同一台服务器上,因此正在调用 localhost。内存使用率约为 25%,cpu 平均使用率约为 10%。

问题出现一段时间后,我开始使用request 模块,但我得到了相同的行为。它在失败之前发出的调用次数似乎在 5 到 100 之间。最后我必须重新启动站点,而不是重新启动 api 才能使其再次工作。

网站中的代码大致如下:

var Request = require('request');
downloadPDF: function(req, res) {
  Project.findById(req.params.Project_id, function(err, project) {
    project.findDoc(req.params.doc_id ,function(err, doc) {
      var pdfileName;
      pdfileName = doc.name + ".pdf";
      res.contentType(pdfileName);
      res.header('Content-Disposition', "filename=" + pdfileName);
      Request("http://localhost:3001/" + project._id).pipe(res);
    });
  });
}

我很了解可能发生的事情。

【问题讨论】:

    标签: node.js request coffeescript express


    【解决方案1】:

    您是否尝试增加 agent.maxSockets 或禁用 http.Agent 功能?默认情况下,最近的节点版本使用套接字池进行 HTTP 客户端连接,这可能是问题的根源 http://nodejs.org/api/http.html#http_class_http_agent

    【讨论】:

    • 我将它从默认的 5 增加到 300,不幸的是它是一样的。
    【解决方案2】:

    我不确定您的 Node 服务器有多忙,但可能是您的所有套接字都处于 TIME_WAIT 状态。

    如果你运行这个命令,你应该看到有多少个套接字处于这个状态:

    netstat -an | awk '/tcp/ {print $6}' | sort | uniq -c
    

    当然,有一些是正常的。您只是不想最大化系统的可用套接字并让它们都在 TIME_WAIT 内。

    如果是这种情况,您实际上希望减少 agent.maxSockets 设置(与@user1372624 的建议相反),否则每个请求都会简单地接收一个新套接字,即使它可以简单地重用最近的一个。达到无响应状态只需要更长的时间。

    我发现这个Gist(http.Agent 的一个补丁)可能会对你有所帮助。

    此服务器故障答案也可能有所帮助:https://serverfault.com/a/212127

    最后,更新 Node 也有可能会有所帮助,因为他们可能已经解决了自您的版本以来的 keep-alive 行为(您可以查看更改日志)。

    【讨论】:

      【解决方案3】:

      您正在使用回调返回一个没有多大意义的值,因为您的 Project.findById() 会立即返回,而无需等待提供的回调完成。

      不过别难过,nodejs 使用的编程模型一开始有点难以理解。

      在事件驱动编程 (EDP) 中,我们提供回调来完成结果,忽略它们的返回值,因为我们永远不知道何时实际调用回调。

      这是一个简单的例子。

      假设我们要将 HTTP 请求的结果写入文件。

      在过程(非 EDP)编程环境中,我们依赖仅在有值返回时才返回值的函数。

      所以我们可能会写类似(伪代码):

          url = 'http://www.example.com'
      
          filepath = './example.txt'
      
          content = getContentFromURL(url)
      
          writeToFile(filepath,content)
      
          print "Done!"
      

      假设我们的程序将等待,直到 getContentFromURL() 联系远程服务器,提出请求,等待 获取结果并将该结果返回给程序。

      writeToFile() 函数然后要求操作系统在某个文件路径打开一个本地文件以进行写入,等待,直到它被告知打开文件操作已完成(通常等待磁盘驱动程序报告它可以执行这样的操作。)

      writeToFile() 然后请求操作系统将内容写入新打开的文件,等待直到被告知操作系统用于写入文件的驱动程序告诉它它已经完成了这个目标,将结果返回给程序,这样它就可以告诉我们程序已经完成了。

      创建nodejs 的目的是为了更好地利用上面发生的所有等待所浪费的所有时间。

      它通过使用函数(回调)来实现这一点,这些函数在诸如从远程 Web 请求中检索结果或将文件写入文件系统等操作完成时被调用。

      要在事件驱动的编程环境中完成上述相同的任务,我们需要编写相同的程序:

          getContentFromURL(url,onGetContentFromURLComplete)
      
          function onGetContentFromURLComplete(content,err){
              writeToFile(content,onWriteToFileComplete);
          }
      
          function onWriteToFileComplete(err){
              print "Done!";
          }
      

      在哪里

      • 调用 getContentFromURL() 仅在获得 Web 请求的结果后调用 onGetContentFromURLComplete 回调并且
      • 调用 writeToFile() 仅在成功完成写入内容时调用其回调以显示成功消息。

      nodejs 的真正魔力在于,它可以在程序函数必须等待以完成大多数时间密集型操作的惊人大量时间中完成各种其他事情(像那些关心输入和输出的)来完成。

      (上面的例子忽略了所有通常被认为是坏事的错误。)

      【讨论】:

      • 我对你的回答投了反对票。虽然我同意贝克在每条语句中都返回的风格是不寻常的,但代码似乎还不错——如果你是对的,我不知道它会如何工作 100 次然后停止工作?
      • 感谢 Rob 的完整回答,内容丰富。最后的返回值是因为它是用coffeescript编写的,我复制并粘贴了编译后的代码,因为js更广为人知。在这种情况下,回报是无关紧要的,因为没有人在使用它。
      【解决方案4】:

      我在使用内置函数时也遇到过间歇性错误。作为一种解决方法,我使用本机 wget。我做了类似以下的事情

      var exec = require('child_process').exec;
      function fetchURL(url, callback) {
          var child;
          var command = 'wget -q -O - ' + url;
          child = exec(command, function (error, stdout, stderr) {
                  callback(error, stdout, stderr);
              });
      }
      

      通过一些小的调整,您可以使其满足您的需求。到目前为止,它对我来说是坚如磐石。

      【讨论】:

      • 谢谢,理想情况下我会继续使用标准的 node.js 函数,但它可能会变成那样。
      【解决方案5】:

      您是否尝试在调用此函数时记录您的参数?错误可能取决于 req.params.Project_id。您还应该在回调函数中提供错误处理。

      如果您可以确定对某个参数集的失败请求(使它们可重现),您可以使用节点检查器轻松调试您的应用程序。

      【讨论】:

      • 你好,好主意。我从示例中删除了日志记录,但每次都会填充 project_id。
      猜你喜欢
      • 2012-01-27
      • 2015-09-03
      • 1970-01-01
      • 2015-06-14
      • 2016-04-05
      • 1970-01-01
      • 2018-06-02
      • 1970-01-01
      相关资源
      最近更新 更多