node.js - 无法追踪 Node JS 内存泄漏
问题描述
在我们的第一个大型 Node/React 同构 Web 应用程序上,我们正在努力解决内存泄漏问题。在 48 小时或更短的时间内,我们使用了超过 500mb 的内存。应用程序所做的大部分工作是从 graphql 服务器获取 json 并使用该数据来制作由 React 组件组成的网页。应用程序中还有一些其他数据源用于特殊内容,但在大多数情况下,应用程序的肉和土豆是对 React 组件的 graphql 查询。我们用来帮助的一些库包括 Express、NextJS 和 Apollo Client。
在我们的服务器上,我们使用 CDN 将 html 页面缓存 60 秒,然后使用 Apache 从端口 443 (ssl) 代理到我们运行节点的端口。
以下是我们试图解决此泄漏的方法:
- 使用 PM2 max-memory-restart 参数在内存不足之前强制重启。这是一个很好的创可贴,但只是一个创可贴。
- 观看了几个关于内存分析的视频,包括:
- 使用 Chrome 的内存分析工具和 node --inspect 完成了多轮内存分析。当我们配置文件时,我们通常会运行一个本地副本并使用 siege 一遍又一遍地点击主页之类的单个路由,如下所示:
siege -c12 -b --no-parser -t1m http://localhost:3000
. 然后我们再次运行相同的东西几次并拍摄内存堆快照。拥有多个堆快照允许我们使用 Chrome 的内存比较工具。在查看内存堆快照时,我从来没有看到我们在应用程序中编写的任何类或函数,只是一大堆对象、数组和字符串等。关于 https、套接字连接、TLS Wrapper 和 Node Parser 和 HTTPParser 似乎确实有很多闲逛。 - 尝试重构以使用具有节点获取和 HTTPS 代理的持久连接
- 尝试重构以降低套接字超时,以便与 graphql/https 请求相关的对象可以更容易地进行垃圾收集。
- 尝试重构以使用 memcached 来防止大量网络连接用于graphql 数据。
- 删除应用程序(站点地图)中最简单的路线,使其成为一个独立的应用程序,我可以对其进行多种变体以轻松进行分析,并使其成为一个独立的应用程序,我可以对其进行多种变体以进行简单的分析。我还对其进行了重构,以确保所有东西都能被车库收集起来。在这里查看:https ://github.com/ghankerson/basic-node-http/blob/master/server.js查看内存缓存分支 https://github.com/ghankerson/basic-node-http/blob/w -memcached/server.js 和 keep alive 分支 https://github.com/ghankerson/basic-node-http/blob/with--https-agent/server.js试)。
我尝试过的其他事情:
- 停止使用 Apollo 客户端的 getDataFromTree,它被认为很慢,并且在过去显然泄漏https://spectrum.chat/next-js/general/next-js-with-apollo-memory-leak~503bb89f-0e59-4db6- accf-e336a9388f47。
- 从应用程序中提取 gzip 并让 Apache 或 CDN 来做。这将内存堆大小减少了大约 2.5 到 3mb,但并没有防止泄漏。
我们即将做的其他事情:
- 停止使用 Node 提供静态资产,让 Apache 来做
- 也许在 Node 应用程序和 graphql 服务器之间放置一个反向代理,并在那里终止 TLS 连接以消除 https 所需的开销。不确定这是否值得。在我的笔记本电脑上用 Nginx 试过这个,但没有看到很大的改进——虽然我不确定我的 https 终止部分是否正确。
到目前为止,我只能做出这些观察:
- 我在这方面花了很多时间,但无法查明内存泄漏。
- 我不确定我的方法是否适合检测泄漏。
- 看起来在主应用程序和人为的站点地图应用程序中,我尝试过的所有变体基本内存配置文件都没有太大变化。在运行 siege 和 profiling 时,内存堆会增加,但最终会在请求停止后几分钟下降。但这并不是在流量稳定的网站上发生的事情——总是有请求进来。似乎堆中用于向graphql服务器发出网络请求的所有东西都交得太久了。我只是不确定这是正确的诊断,如果它是怎么做的。
如果有帮助,我可以发布内存堆快照。真的希望有人能指出我哪里出错了,或者至少提供一些好的提示。
最初的反馈是获取 prod 堆快照,所以我现在有一些 prod 堆快照来解释它们是一个挑战,因为我读到这很难解释。这都是匿名流量,所以如果您想查看这里的快照,它们是:第一个、第二个和最后一个,它们使用 bzip2 压缩以减少下载大小。第一个是在部署之后,所以节点重新启动,下一个是从那天晚上开始,最后一个是第二天早上
这是上一张截图与上一张截图的对比
让这变得困难的是我看不到我们在那里写的代码,除非我钻进去
解决方案
在这种情况下,答案是从应用程序中删除 Express。我们只使用 Express 进行路由,幸运的是 NextJs 几乎同时实现了自己的路由。我们所做的唯一另一件事是将 api 调用移至 NWS 天气 api 调用,使其成为 100% 客户端。
之后,内存泄漏消失了。
推荐阅读
- woocommerce - Woocommerce 订阅手动付款
- vue.js - 我的 html 代码无法访问我的 vue 组件
- c# - SQLDataReader。ExecuteReader 超时
- c# - 多个对象的刚体
- java - 如何将二维字符串数组从 java 代码传递到 play 框架中的 scala.html?
- r - geom_density_2d 中的轮廓太紧
- spartacus-storefront - 是否建议使用 TypeScript 为 Spartacus 店面编写 Angular 的东西?
- javascript - 如何预加载字体?
- java - getIntent().getStringExtra("name") 返回 null
- java - 如何修复 fxml 中的“无法为 .jar 文件派生模块描述符”错误