【问题标题】:Node.js+Express Randomly Drops Requests, resulting in a Gateway TimeoutNode.js+Express 随机丢弃请求,导致网关超时
【发布时间】:2012-10-12 22:44:58
【问题描述】:

编辑

经过一番折腾,我终于找到了一些看起来可能是可靠的线索:

当 express 库当前正在使用 Node+OAuth 模块执行多个出站请求(例如,发往 Facebook、Twitter 等)时,它无法接受传入请求。我可以通过在我的代码中放置大量日志来确定这一点,我发现在出站请求中间没有触发“begin-request”日志(如下所述)。

我已经能够证明,当 Node+OAuth 模块发出一些出站请求时,对我的 API 的入站请求(通过浏览器窗口)将挂起并且直到其中一个出站 OAuth 请求被接收完成了。

当然,我已经完成了:

require('http').globalAgent.maxSockets = 999;

根据 IRC 中的建议,我已添加

console.log(require('http').globalAgent.requests);

但这似乎总是 === {},这意味着没有待处理的入站请求 AFAIK。

因此,我只能得出结论,由于出站请求,node.js 或 express 选择阻止传入请求,即使应该有大量可用的套接字......

有人对如何解决这个问题有任何提示吗?


我有一个使用 Express、Mongoose 等在 node.js 中创建的 API,部署在 Amazon Cloud 上,它在 99% 的时间里运行良好且快速。

除了偶尔,请求似乎以某种方式被丢弃或以其他方式忽略。我说的是通常在几毫秒内完成的请求,随机无响应,没有清晰的画面为什么

症状是连接到 API 端点时出现简单的“网关超时”。相同的请求,由相同的客户端发出,具有相同的参数,就在之前或之后,都可以正常工作。

当然,我的第一个想法是“呃,服务器过载!”所以我花了很多时间优化我的请求、mongoDB 等。最后我明白了 CPU/磁盘/RAM 的全面使用(在 Node.js 服务器和 Mongo 服务器中)是非常低。我使用 Scout 和 RightScale 实时跟踪我的服务器,并记录任何超过 100 毫秒的请求或查询。我的节点服务器目前有 5GB 的可用 RAM、70% 的可用 CPU(在第一个核心上)等。所以我 99.99% 确定这不是性能问题。

最后,我绝望地尝试了:我为客户提出的所有请求附加了一个随机数。然后,在 node.js 应用程序中,我在第一次收到请求并完成时执行 console.log()。例如,这是我在 express 中使用的中间件:

var configureAPI = function() {
    return function(req, res, next) {
        if(req.body.ruid)
            console.log(req.body.ruid);

        // more middleware stuff...
    };
}
server.configure(function(){

    server.use(express.bodyParser());
    server.use(configureAPI());
    server.use(onError);

    // ...  more config stuff
}

我的发现让我震惊:显然,node.js 应用甚至没有收到相关请求。我有一个 Javascript webapp,我打印了随请求发送到控制台的“ruid”。每当请求成功时,都会在 node.js 控制台中打印出相应的“ruid”。只要超时,就没有。


编辑:更多调试和信息。

我的应用服务器实际上开始(并继续)也为 PHP 提供服务(因此,它们安装了 Apache 等)。我需要http://streamified.me 来服务我的网站(PHP)和http://api.streamified.me 来服务我的API(node.js)......所以我的httpd.conf 文件中有一行导致对api.streamified.me 的请求(而不是streamified.me) 通过 8888 端口访问 node.js:

RewriteCond %{HTTP_HOST} ^api.streamified.me
RewriteRule ^(.*) http://localhost:8888$1 [P]

所以,在同一个 httpd.conf 文件中,我打开了 RewriteLogLevel 5,然后在我的本地主机上创建了一个简单的 PHP+CURL 脚本,以使用随机 URL 访问我的 api.streamified.me(这应该会导致 node.js触发一个简单的“未找到”响应),直到导致网关超时。在这里,您可以看到它已经发生了——并且重写日志显示该请求肯定被应用服务器接收并转发到端口 8888 ......但它从未被 node.js 接收(或者,至少,中间件第一行中的第一行代码永远不会得到它......)


我已经一遍又一遍地检查了我的 node.js 代码,并且很确定我没有阻塞代码,即使我有,我也无法想象它阻塞线程的时间足够长以至于错过了一个请求而不引发红色在某处标记。

我错过了什么?传入套接字是否有某些原因会被阻塞?我确实通过我的 node.js 应用程序向外部 API 发出了相当数量的 HTTP 请求,但 AFAIK 不应该阻塞传入的套接字。


当然,我有错误日志记录。我已经在进程级别启用了它...

process.addListener("uncaughtException", function (err) {
    // some logging code
}

在 Express 级别(上面的 onError 处理程序)。我知道我的错误记录功能可以工作,因为我以前见过它们都触发过。但是他们都没有在请求被丢弃的时候报告任何东西,我也没有在控制台中看到任何东西......


  • Express 版本:3.0.0rc5
  • Node.js 版本:0.8.12
  • 在标准 Amazon Cloud 设置(m1.large 实例)上运行的 2 个 node.js 应用程序实例,位于 2 个负载均衡器之后,连接到 3 个 MongoDB 副本集(也是 m1.large)

【问题讨论】:

  • 您已确认负载均衡器正在接收请求并将其成功发送到您的节点服务器?当请求失败时,您多久发出一次请求?
  • 同样的负载均衡/应用服务器也提供 PHP 文件,不会导致超时。不过,除此之外,我不太确定如何确认 LB 是否正确转发到节点服务器。我没有显示任何流量高峰; apache 登录 Rightscale 报告一致的 ~10 req/sec。
  • 我发现列出的几个错误描述了类似的问题,但它们都已在 0.6.6 中修复。您可以尝试升级到最新版本,因为自 0.6 以来已经有大量的修复/改进。我还建议您在应用服务器上设置网络嗅探器,以确保服务器实际接收数据包。
  • 我正在努力升级到最新的节点版本,但与此同时,对于如何在我的应用服务器上实现数据包嗅探器,您有什么好的链接/建议吗?这对我来说是新的:(
  • 如果您是新手,我会在 serverfault.com 上询问。

标签: node.js


【解决方案1】:

听起来您锁定 Node 线程的时间过长,导致传入连接在处理它们之前超时。 Node 是单线程的,所以它一次只做一件事,它不能因为传出请求而选择阻止传入请求。它只能因为忙于做其他事情而无法接受传入的请求。你需要弄清楚它在忙什么。

如果您不发出出站请求,一切正常吗?如果是这样,您需要查看发出这些请求的代码,以确保您没有等待响应。

【讨论】:

  • 这是有道理的。我很肯定我没有做任何“同步”任务,我使用 Q 来实现承诺。唯一想到的是 JSON.parse() 命令,它们正在评估大 (~2MB) 数据字符串。这些操作会阻塞线程吗?
  • 某些数据返回的大小是否超过 2MB? 2MB 不应该让线程挂起足够长的时间来丢弃请求(尽管它会阻塞一些东西),但如果你偶尔尝试解析更大的字符串,它可能是罪魁祸首。您可以尝试用返回静态数据的调用替换解析,看看是否能解决问题。
  • 嗯,我不能真正使用静态数据,因为 JSON 解析随后会影响下游的事情,并且通过使用静态数据,我只会测试 1 个场景......无论如何,我开始输出 parse() 时间,有时约为 100 毫秒(通常有几个背靠背,尽管由承诺分隔)。此外,我刚刚第一次出现 OOM 错误“致命错误:CALL_AND_RETRY_2 分配失败 - 进程内存不足”......这让我想知道这是否可能是问题的一部分......虽然这是我第一次见过这样的错误...
  • 我只是想用静态数据替换作为测试,看看解析是否有问题。堵那么久,绝对不是什么好事。如果你的内存不足,听起来你有一个更大的问题。是时候打开一个新问题并发布您用于发出请求和解析返回值的代码了。
猜你喜欢
  • 2013-12-20
  • 2015-09-06
  • 1970-01-01
  • 2017-07-24
  • 1970-01-01
  • 1970-01-01
  • 2021-04-28
  • 2014-04-25
  • 1970-01-01
相关资源
最近更新 更多