Node.js应用程序有周期性的缓慢和/或超时(不接受传入的请求)
这个问题正在破坏我的生产服务器的稳定性。
回顾一下,基本思想是我的节点服务器有时会间歇性地减速,有时会导致网关超时。 就像我从日志中可以看出的那样,有些东西阻塞了节点线程(意味着传入的请求不被接受),但是我不知道是什么原因。
问题的严重程度不一 。 有时应该是<100ms请求需要10秒钟完成; 有时他们甚至从来没有被节点服务器接受。 简而言之,就好像一些随机任务正在工作并阻塞节点线程一段时间,从而减缓(甚至阻塞)传入的请求; 我可以肯定的一件事是,需要修复的症状是“网关超时” 。
这个问题来不加警告 。 我无法将其与CPU使用率,RAM使用率,正常运行时间或任何其他相关的统计数据相关联。 我已经看到服务器处理大负载罚款,然后有一个小负载这个错误,所以它甚至不会是负载相关的。 在太平洋标准时间上午1点左右发现错误并不罕见,这是一天中最小的加载时间! 重新启动节点应用程序似乎可能会让问题消失一段时间 ,但是这并不能说明问题。 我不知道是否它可能是node.js中的错误 …不是很让人安慰,考虑到它正在杀死我的生产服务器。
- 我做的第一件事是确保我已经升级node.js到最新的(0.8.12),以及我所有的模块( 在这里 )。 当然,我也有很多错误捕捉器。 我没有做任何事情,比如打印出大量的控制台或写入大量的文件。
- 起初, 我认为这是出站HTTP请求阻塞传入的套接字,因为expression中间件甚至没有收到入站请求,但我放弃了理论,因为它看起来像节点线程本身变得繁忙 。
- 接下来,我使用JSHint检查了所有的代码,并逐字修改了每一个警告,包括一些意外的全局variables(忘记写“var”),但是这并没有帮助
- 之后,我认为也许我的记忆力已经耗尽。 但是,我通过nodetime的堆快照现在看起来相当不错(如下所述)。
- 仍然认为内存可能是一个问题,我看了一下垃圾收集 。 我启用了–nouse-idle-notification标志,并在不需要的时候对NULL对象做了一些代码优化。
- 仍然相信内存是问题,我添加了–expose-gc标志并执行了gc(); 每分钟命令。 这并没有改变任何东西,除了偶尔提出要求稍微慢一些。
- 在绝望的尝试,我设置“群集”模块使用2名工人,并自动重新启动,每30分钟。 但是,没有运气。
- 我把ulimit增加到了10,000以上,并且关注了打开的文件。 每个node.js应用似乎有<300个打开文件(或套接字),因此增加ulimit没有影响。
我一直使用nodetime来logging我的服务器,下面是它的主题:
- 在Amazon云上运行的CentOS 5.2(m1.large实例)
- 任何时候都大于5000 MB的可用内存
- 在任何时候小于150 MB的堆大小
- CPU使用率始终低于60%
我也检查了我的MongoDB服务器,这些服务器的CPU使用率小于5%,没有任何请求花费大于100ms来完成,所以我非常怀疑这是一个瓶颈。
我用Q-promises( 见代码示例 )包装(几乎)所有的代码,当然也避免了像鼠疫这样的Sync()调用。 我试图在我的testing服务器(OSX)上复制这个问题,但运气不大。 当然,这可能只是因为生产服务器被许多人以无法预料的方式被使用,我根本无法通过压力testing来复制…
我的猜测是mongoose。 如果您在Mongo中存储大量的有效载荷,Mongoose可能会很慢,因为它如何构buildMongoose对象。 有关该问题的更多详细信息,请参阅https://github.com/LearnBoost/mongoose/issues/950 。 如果这是问题,你不会看到它在Mongo本身,因为查询返回很快,但对象实例化可能需要75倍的查询时间。
尝试在创buildMongoose对象之前和之后设置定时器( process.hrtime()
),看看是否可能是问题所在。 如果这是问题,我将直接使用节点Mongo驱动程序而不是通过Mongoose。
我第一次问这个问题几个月后,我find了答案。
简而言之,问题在于当我从一台服务器转移到另一台服务器时,我没有提供很大的资产。 换句话说,我是从一台服务器下载一个镜像,然后上传到S3存储桶。 我没有将下载stream式传输到上传文件中,而是将文件下载到内存中,然后上传。
我不知道为什么这不会显示为记忆秒杀,或在我的统计其他地方。
你正在大量泄漏内存,只要你不需要它,就尽量将每个对象设置为null! 阅读这个 。
有关寻找内存泄漏的更多信息可以在这里find。
要特别注意对同一个对象有多个引用,并检查是否有循环引用,这是一个很难debugging,但会帮助你非常。
尝试每分钟手动调用垃圾收集器(我不知道你是否可以在node.js中这样做,因为我更像是一个c ++和php编码器)。 从我多年使用c ++的经验中,我可以告诉你,随着时间的推移,应用程序的最可能的原因是内存泄漏,find它们并将它们插入,你会没事的!
另外假设你没有caching和/或处理图像,内存中的audio或video或类似的150M堆是很多! 那些可能是数十万甚至数百万的小物件。
您不必为内存不足而导致应用程序的运行速度变慢……仅仅使用已分配的许多对象来search可用内存对于内存分配器来说是一项巨大的工作,需要花费大量的时间来分配每个新的内存对象,当你泄漏越来越多的内存时间只增加。
是“ – nouse-idle-connection”错误吗? 你真的是指“–nouse_idle_notification”。
我认为这可能是一些关于gc的问题,太多的小东西。 节点是单进程的,所以看最繁忙的cpu核心比负载重要得多。 当你的程序很慢的时候,你可以执行“gdb node pid”和“bt”来查看哪个节点正在忙着做什么。
我要做的是在同一台服务器上设置一个并行节点实例,并使用某种回声服务来testing该实例。 如果运行良好,则将问题缩小到程序代码(而不是调度程序/操作系统级别的问题)。 然后,一步一步地,包含模块并再次testing。 当然这是很多的工作,需要很长时间,我不知道你的系统是否可行。
如果你现在需要这个工作,你可以去NASA冗余路线:
调出生产服务器的第二个副本,并在它们前面放置一个代理,将每个请求路由到这两个堆栈并返回第一个响应。 我不build议这是一个完美的长期解决scheme,但它应该有助于显着减less生产中的问题,并帮助您收集日志数据,以便在非生产服务器上重新创build问题。
显然,对于读取请求来说,这是直接的,但是对于写入数据库的命令更复杂。
我们的Node.js服务器也有类似的问题。 它几周没有好的规模,我们已经尝试了几乎所有的东西。 我们的问题是在高并发环境下隐含的积压值被设置得非常低 。
http://nodejs.org/api/http.html#http_server_listen_port_hostname_backlog_callback
将积压工具设置为一个更高的值(例如10000)以及调整内核中的networking(Linux上的/etc/sysctl.conf),这在手册部分中有所帮助。 从现在开始,我们在Node.js服务器中没有任何超时。