垃圾收集导致连接的套接字延迟(NodeJS 服务器)
Posted
技术标签:
【中文标题】垃圾收集导致连接的套接字延迟(NodeJS 服务器)【英文标题】:Garbage collection causes lag on connected sockets (NodeJS Server) 【发布时间】:2020-03-09 22:44:45 【问题描述】:我在 Heroku 上托管一个运行在 NodeJS 服务器上的游戏网站。客户端通过使用包socket.io 的套接字连接。
偶尔触发垃圾回收周期时,连接的客户端会遇到严重的延迟,并且经常会出现断开连接。客户通过延迟的传入聊天和延迟的游戏输入体验到这一点。
当我查看日志时,我发现了与垃圾收集相关的错误消息。请参阅下面的附加日志。当这些 GC 事件发生时,有时它会导致大量内存峰值,以至于应用程序超过允许的 0.5GB RAM 并被 Heroku 杀死。然而,最近,内存峰值不再经常发生,但客户端的严重延迟仍然每天大约发生一两次。
延迟的一个方面是通过聊天。当用户通过“所有聊天”(和任何聊天频道)键入消息时,服务器当前console.log()
将其发送到标准输出。我碰巧在峰值事件期间观看了一次实时日志,并注意到实时输出到终端的聊天没有延迟,但是客户(我自己也是网站上的客户)收到了这些消息很延迟的时尚。
我在网上发现了一个 NodeJS 错误(我认为已修复),当太多console.log
ed 显示在屏幕上时会导致严重滞后,所以我通过每秒从客户端发送 1000 条消息来运行压力测试,一分钟。我无法重现尖峰。
我已经阅读了许多关于查找内存泄漏、检查堆栈等的指南,但我非常不确定如何在实时 Heroku 服务器上运行这些测试。我怀疑我的游戏对象在关闭时没有立即被清除,而是一次全部清除,导致内存峰值,但我不自信。我不知道如何最好地调试它。我也很难实时捕捉到这种情况,因为它只在超过 30 人登录时才会发生(不经常发生,因为这仍然是一个相当小的站点)。
错误消息包括对我使用的circular-json
模块的引用,并且我还怀疑这可能会以某种方式导致自身无限回调并且无法正确清除,但我不确定。
供参考,这里是源代码的副本:LINK
这是一个峰值发生时内存的 sn-p: Memory spike
崩溃日志 1:HERE
崩溃日志 2:HERE
有没有一种方法可以在本地模拟套接字或模拟实时服务器的环境(即连接的客户端)?
任何有关如何解决或调试此问题的建议将不胜感激。谢谢。
【问题讨论】:
【参考方案1】:需要考虑的是,console.log 会增加内存使用量。如果您使用大量数据详细记录,这可能会累积。快速查看日志,您似乎内存不足?这意味着应用程序开始写入速度较慢的磁盘,并且还会运行垃圾收集尖峰 CPU。
这可能意味着内存泄漏,因为资源没有被杀死/关闭并且只是积累。调试它可以是一个 PITA。
Node 使用 1.5GB 来保存长寿命对象。好像你在一个 500mb 的容器上,所以最好配置 web 应用程序来启动:
web: node --optimize_for_size --max_old_space_size=460 server.js
虽然您需要深入了解漏洞,但您还可以通过运行多个工作程序和多个节点实例来提高可用性,并使用socket.io-redis 保持实例同步。我强烈推荐这条路线。
Some helpful content on Nodejs memory on Heroku.
您还可以通过节点脚本启动多个连接,以使用socket.io-client 与您的本地开发服务器交互,并在本地监控内存并添加日志记录以确保正确关闭连接等。
【讨论】:
感谢您的回复!我不知道 NodeJS 的 1.5GB 默认 RAM 设置,这表明我应该完全阅读 Heroku NodeJS 内存页面!我将使用建议的配置进行测试。运行多个同步服务器绝对是我的长期目标之一,但我还没有足够的知识去做。谢谢你指点我这个方向,我会调查的!使用节点脚本来模拟多个套接字听起来是个好主意。我将使用它进行测试,看看我是否可以找到内存泄漏。感谢您的时间和帮助! 没有问题。刚刚完成了上述所有内容,这是一款使用上述套接字和集群的非常健谈的 RTS'ish 游戏。【参考方案2】:我最终设法追踪了我的“内存泄漏”。事实证明,我过于频繁地将游戏(以 JSON 化字符串形式)保存到数据库中,并且服务器/数据库跟不上。我降低了游戏存档的频率,没有遇到任何问题。
Samuel 提供的建议也很有帮助。
【讨论】:
以上是关于垃圾收集导致连接的套接字延迟(NodeJS 服务器)的主要内容,如果未能解决你的问题,请参考以下文章
如何使用套接字(socket.io)在 NodeJS 中找到客户端的响应时间(延迟)?