Node.js应用程序有周期性的缓慢和/或超时(不接受传入的请求)

时间:2012-10-14 20:54:48

标签: node.js

此问题正在破坏我的生产服务器的稳定性。

总结一下,基本思路是我的节点服务器有时会间歇性地减速,有时会导致网关超时。正如我从日志中可以看出的那样,有些东西阻塞了节点线程(意味着传入的请求不被接受),但我不能为我的生活找出什么。

问题的严重程度。有时应该< 100ms请求需要大约10秒才能完成;有时它们甚至从未被节点服务器接受。 简而言之,就好像一些随机任务正在工作并阻塞节点线程一段时间,从而减慢(甚至阻塞)传入的请求;我可以肯定的一点是,需要修复的症状是“网关超时”

问题出现并且没有警告。我无法将其与CPU使用率,RAM使用率,正常运行时间或任何其他相关统计信息相关联。我已经看到服务器处理大的负载很好,然后在小负载下出现这个错误,所以它甚至看起来与负载无关。在太平洋标准时间凌晨1点左右看到错误并不罕见,这是当天最小的加载时间!重新启动节点应用似乎可能让问题消失一段时间,但这真的不会告诉我太多。我确实想知道it might be a bug in node.js ......不是很安慰,因为它正在杀死我的生产服务器。

我一直在使用nodetime记录我的服务器,这里是它的主旨:

  • 在Amazon Cloud上运行的CentOS 5.2(m1.large实例)
  • 始终提供超过5000 MB的可用内存
  • 始终小于150 MB的堆大小
  • CPU使用率始终低于60%

我还检查了我的MongoDB服务器,它们的CPU使用率低于5%且没有请求> 100毫秒完成,所以我高度怀疑是否存在瓶颈。

我使用Q-promises(see code sample)包装(几乎)所有代码,当然也避免了像瘟疫这样的Sync()调用。我试图在我的测试服务器(OSX)上复制这个问题,但运气不好。当然,这可能只是因为生产服务器被许多人以如此多的不可预测的方式使用,我无法通过压力测试来复制......

7 个答案:

答案 0 :(得分:10)

我的猜测是猫鼬。如果您在Mongo中存储大型有效负载,Mongoose可能会因为它构建Mongoose对象而变得非常慢。有关该问题的详细信息,请参阅https://github.com/LearnBoost/mongoose/issues/950。如果这是问题,你不会在Mongo本身看到它,因为查询很快返回,但是对象实例化可能需要75倍的查询时间。

尝试在创建Mongoose对象之前和之后设置计时器(process.hrtime())以查看是否存在问题。如果这是问题,我将切换到直接使用节点Mongo驱动程序而不是通过Mongoose。

答案 1 :(得分:10)

在我第一次提出这个问题后的几个月,我找到了答案。

简而言之,问题在于,当从一台服务器转移到另一台服务器时,我没有管理大资产。换句话说,我是在从一台服务器下载图像之前将其上传到S3存储桶。我没有将下载流式传输到上传中,而是将文件下载到内存中,然后上传。

我不确定为什么这不会显示为内存峰值或我的统计数据中的其他位置。

答案 2 :(得分:4)

你正在大量泄漏内存,一旦你不再需要它就尝试将每个对象设置为null! Read this

有关追捕内存泄漏的更多信息,请访问here

特别注意对同一个对象进行多次引用并检查是否有循环引用,这些都很难调试,但会对你有所帮助。

尝试每分钟左右手动调用垃圾收集器(我不知道你是否可以在node.js中执行此操作,因为我更像是一个c ++和php编码器)。根据我多年使用c ++的经验,我可以告诉你,随着时间的推移,应用程序减速的最可能原因是内存泄漏,找到并插入它们,你会没事的!

另外假设您没有缓存和/或处理内存中的图像,音频或视频或类似150M堆的任何内容!那些可能是数十万甚至数百万个小物件。

你的应用程序没有必要耗尽内存来减速...只是为了内存分配器而已经分配了许多对象来搜索空闲内存是一项巨大的工作,它需要花费大量时间分配每个新对象,当你泄漏越来越多的内存,时间只会增加。

答案 3 :(得分:1)

“--nouse-idle-connection”是一个错误吗?你的意思是“--nouse_idle_notification”。

我认为这可能是gc中存在太多微小物体的一些问题。 节点是单进程,因此观看最繁忙的cpu核心比负载重要。 当程序运行缓慢时,可以执行“gdb node pid”和“bt”来查看正在忙于执行的节点。

答案 4 :(得分:1)

我要做的是在同一台服务器上使用某种echo服务设置一个并行节点实例并测试一个。如果它运行正常,您可以将问题缩小到程序代码(而不是调度程序/操作系统级别的问题)。然后,逐步包含模块并再次测试。当然这需要很多工作,需要很长时间,我不知道你的系统是否可行。

答案 5 :(得分:1)

如果你现在需要这个工作,你可以去美国宇航局的冗余路线:

打开生产服务器的第二个副本,并在它们前面放置一个代理,将每个请求路由到两个堆栈并返回第一个响应。我不建议将其作为完美的长期解决方案,但它应该有助于显着减少现在的生产问题,并帮助您收集可以重播的日志数据,以便在非生产服务器上重新创建问题。

显然,这对于读取请求是直截了当的,但对于写入db的命令则更为复杂。

答案 6 :(得分:0)

我们的Node.js服务器遇到了类似的问题。它几周没有很好的扩展,我们已经尝试了几乎所有的东西。我们的问题出在隐含的积压值,对于高并发环境,该值设置为非常低

http://nodejs.org/api/http.html#http_server_listen_port_hostname_backlog_callback

将积压设置为显着更高的值(例如10000)以及在内核中调整网络(Linux上的/etc/sysctl.conf),如手册部分所述,有很多帮助。从现在开始,我们的Node.js服务器没有任何超时。