在本文中,我将分享我在 Node.js 中跟踪和修复高内存使用率的方法。
内容
- 上下文
-
方法
- 理解代码
- 单独复制问题
- 从暂存服务捕获配置文件
- 验证修复
- 结果
- 结论
语境
最近我收到了一张标题为“修复库 x 中的内存泄漏问题”的票证。该描述包括一个 Datadog 仪表板,其中显示了十几个因内存使用率过高并最终因 OOM(内存不足)错误而崩溃的服务,并且它们都有共同的 x 库。
我最近才接触到代码库(不到 2 周),这使得这项任务充满挑战性,也值得分享。
我开始使用两条信息:
- 有一个所有服务都使用的库导致内存使用率很高,它涉及到redis(库的名称中包含redis)。
- 受影响的服务列表。
下面是链接到票证的仪表板:
服务在 Kubernetes 上运行,很明显,服务会随着时间的推移积累内存,直到达到内存限制、崩溃(回收内存)并重新启动。
方法
在本节中,我将分享我如何处理手头的任务,找出高内存使用率的罪魁祸首并随后修复它。
理解代码
由于我对代码库相当陌生,我首先想了解代码、相关库的作用以及它应该如何使用,希望通过这个过程可以更容易地识别问题。不幸的是,没有适当的文档,但通过阅读代码和搜索服务如何利用该库,我能够理解它的要点。它是一个围绕 redis 流的库,并为事件生成和消费提供方便的接口。花了一天半的时间阅读代码,由于代码结构和复杂性(很多我不熟悉的类继承和rxjs),我无法掌握所有细节以及数据如何流动。
因此我决定暂停阅读,并在观察代码运行情况并收集遥测数据的同时尝试发现问题。
单独复制该问题
由于没有可用的分析数据(例如连续分析)可以帮助我进一步调查,因此我决定在本地复制该问题并尝试捕获内存配置文件。
我发现了几种在 Node.js 中捕获内存配置文件的方法:
- 使用堆快照
- 使用堆分析器
- JavaScript 性能分析
- Clinic.js
由于不知道去哪里寻找,我决定运行我认为是库中最“数据密集”的部分,即 redis 流生产者和消费者。我构建了两个简单的服务,它们可以生成和使用来自 redis 流的数据,然后我继续捕获内存配置文件并比较一段时间内的结果。不幸的是,在对服务产生负载并比较配置文件几个小时后,我无法发现这两个服务中任何一个服务的内存消耗有任何差异,一切看起来都很正常。该库公开了一系列不同的接口以及与 Redis 流交互的方式。我很清楚,复制该问题比我预期的要复杂得多,尤其是在我对实际服务的特定领域知识有限的情况下。
所以问题是,如何找到合适的时机和条件来捕获内存泄漏?
从暂存服务捕获配置文件
如前所述,捕获内存配置文件的最简单、最方便的方法是对受影响的实际服务进行连续分析,但我没有这个选项。我开始研究如何至少利用我们的暂存服务(它们面临同样的高内存消耗),这将使我无需额外的努力即可捕获所需的数据。
我开始寻找一种将 Chrome DevTools 连接到其中一个正在运行的 Pod 并随着时间的推移捕获堆快照的方法。我知道内存泄漏发生在暂存阶段,因此,如果我能够捕获该数据,我希望能够至少发现一些热点。令我惊讶的是,有一种方法可以做到这一点。
执行此操作的过程
- 通过向 pod 上的节点进程发送 SIGUSR1 信号来启用 pod 上的 Node.js 调试器。
kubectl exec -it <nodejs-pod-name> -- kill -SIGUSR1 <node-process-id> </node-process-id></nodejs-pod-name>
更多关于 Signal Events 中的 Node.js 信号
如果成功,您应该会看到来自服务的日志:
Debugger listening on ws://127.0.0.1:9229/.... For help, see: https://nodejs.org/en/docs/inspector
- 通过运行在本地公开调试器正在侦听的端口
kubectl port-forward <nodejs-pod-name> 9229 </nodejs-pod-name>
- 将 Chrome Devtools 连接到您在之前步骤中启用的调试器。访问 chrome://inspect/,您应该在目标列表中看到您的 Node.js 进程:
如果没有,请确保您的目标发现设置正确设置
现在您可以开始捕获超时快照(时间段取决于发生内存泄漏所需的时间)并进行比较。 Chrome DevTools 提供了一种非常方便的方法来做到这一点。
您可以在记录堆快照中找到有关内存快照和 Chrome 开发工具的更多信息
创建快照时,主线程中的所有其他工作都会停止。根据堆内容,甚至可能需要一分多钟的时间。快照内置在内存中,因此它可以使堆大小加倍,从而导致填满整个内存,然后使应用程序崩溃。
如果您要在生产中获取堆快照,请确保从中获取快照的进程可以崩溃,而不会影响应用程序的可用性。
来自 Node.js 文档
回到我的例子,选择两个快照进行比较并按增量排序,我得到了您在下面看到的内容。
我们可以看到最大的正增量发生在字符串构造函数上,这意味着该服务在两个快照之间创建了很多字符串,但它们仍在使用中。现在的问题是它们是在哪里创建的以及谁在引用它们。幸运的是,捕获的快照包含了这些信息,也称为 Retainers。
在深入研究快照和永不缩小的字符串列表时,我注意到一种类似于 id 的字符串模式。单击它们,我可以看到引用它们的链对象 - 又名保留器。这是一个名为 sendEvents 的数组,其类名是我可以从库代码中识别出来的。哎呀,我们找到了罪魁祸首,一个不断增长的 id 列表,到目前为止我认为这些列表从未被发布过。我超时拍摄了一堆快照,这是唯一一个不断重新出现为具有较大正增量的热点的地方。
验证修复情况
有了这些信息,我不需要尝试完全理解代码,而是需要关注数组的用途、何时填充和何时清除。在一个地方,代码将项目推送到数组,而在另一个地方,代码将项目弹出,这缩小了修复的范围。
可以安全地假设数组在应该清空的时候没有被清空。跳过代码的细节,基本上发生的事情是这样的:
- 该库公开了用于消费、生成事件或生成和消费事件的接口。
- 当它既消耗又产生事件时,它需要跟踪进程本身产生的事件,以便跳过它们而不重新消耗它们。 sendEvents 在生成时被填充,并在尝试使用时被清除,它会跳过消息。
你能看出这是怎么回事吗? ?当服务仅使用该库来生成事件时,sentEvents 仍会填充所有事件,但没有代码路径(使用者)用于清除它。
我修补了代码以仅跟踪生产者、消费者模式上的事件并部署到登台。即使存在暂存负载,很明显该补丁也有助于减少高内存使用率,并且没有引入任何回归。
结果
当补丁部署到生产环境中时,内存使用量大幅减少,服务的可靠性得到提高(不再出现 OOM)。
一个很好的副作用是处理相同流量所需的 Pod 数量减少了 50%。
结论
对于我来说,这是一个很好的学习机会,可以跟踪 Node.js 中的内存问题并进一步熟悉可用的工具。
我认为最好不要详细讨论每个工具的细节,因为这值得单独发表一篇文章,但我希望这对于任何有兴趣了解更多有关此主题或面临类似问题的人来说是一个很好的起点。
以上是跟踪 Node.js 中的高内存使用率的详细内容。更多信息请关注PHP中文网其他相关文章!

Python和JavaScript的主要区别在于类型系统和应用场景。1.Python使用动态类型,适合科学计算和数据分析。2.JavaScript采用弱类型,广泛用于前端和全栈开发。两者在异步编程和性能优化上各有优势,选择时应根据项目需求决定。

选择Python还是JavaScript取决于项目类型:1)数据科学和自动化任务选择Python;2)前端和全栈开发选择JavaScript。Python因其在数据处理和自动化方面的强大库而备受青睐,而JavaScript则因其在网页交互和全栈开发中的优势而不可或缺。

Python和JavaScript各有优势,选择取决于项目需求和个人偏好。1.Python易学,语法简洁,适用于数据科学和后端开发,但执行速度较慢。2.JavaScript在前端开发中无处不在,异步编程能力强,Node.js使其适用于全栈开发,但语法可能复杂且易出错。

javascriptisnotbuiltoncorc; saninterpretedlanguagethatrunsonenginesoftenwritteninc.1)javascriptwasdesignedAsalightweight,解释edganguageforwebbrowsers.2)Enginesevolvedfromsimpleterterterpretpreterterterpretertestojitcompilerers,典型地提示。

JavaScript可用于前端和后端开发。前端通过DOM操作增强用户体验,后端通过Node.js处理服务器任务。1.前端示例:改变网页文本内容。2.后端示例:创建Node.js服务器。

选择Python还是JavaScript应基于职业发展、学习曲线和生态系统:1)职业发展:Python适合数据科学和后端开发,JavaScript适合前端和全栈开发。2)学习曲线:Python语法简洁,适合初学者;JavaScript语法灵活。3)生态系统:Python有丰富的科学计算库,JavaScript有强大的前端框架。

JavaScript框架的强大之处在于简化开发、提升用户体验和应用性能。选择框架时应考虑:1.项目规模和复杂度,2.团队经验,3.生态系统和社区支持。

引言我知道你可能会觉得奇怪,JavaScript、C 和浏览器之间到底有什么关系?它们之间看似毫无关联,但实际上,它们在现代网络开发中扮演着非常重要的角色。今天我们就来深入探讨一下这三者之间的紧密联系。通过这篇文章,你将了解到JavaScript如何在浏览器中运行,C 在浏览器引擎中的作用,以及它们如何共同推动网页的渲染和交互。JavaScript与浏览器的关系我们都知道,JavaScript是前端开发的核心语言,它直接在浏览器中运行,让网页变得生动有趣。你是否曾经想过,为什么JavaScr


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

VSCode Windows 64位 下载
微软推出的免费、功能强大的一款IDE编辑器

SublimeText3 英文版
推荐:为Win版本,支持代码提示!

SublimeText3 Linux新版
SublimeText3 Linux最新版

Dreamweaver CS6
视觉化网页开发工具