Next.js 应用的内存泄漏的排查和解决
项目一个 Next.js 应用版本 13.5.4,在日活快过万的时候,观察了一下 grafana 的内存走势,有明显的上升趋势,而且,没有任何释放的意思,很明显的,内存泄漏了
24 小时内存曲线:
2 日内存曲线:
排查过程
首先,在本地启动项目,按生产环境的方式启动尽量模拟生产,我这边用到了 Next.js 的 standalone 模式,所以本地启动步骤有点多。注意 node 后带 --inspect,是为了在 chrome 里调试 nodejs,参考 nodejs 的调试文档
npm run build
cp -r ./public ./.next/standalone && cp -r .next/static .next/standalone/.next && node --inspect ./.next/standalone/server.js
执行后,打开 h5,会发现这里多了个 node 的调试图标,点开他,然后切到 memory tab,可以看到,当前 node 应用的内存情况:
现在需要确定是哪个页面泄漏。这里用到一个比较有名且简单的压测命令行工具wrk,我环境是 Ubuntu,直接 apt 安装就行
sudo apt install wrk
内存监控已经待命,开始对首页进行 DDOS,下面命令意思是,开 10 个线程,400 个 http 连接,保持 30 秒,大概 QPS 为 35
wrk -t10 -c400 -d30s http://localhost:3000
执行后的 30 秒内,每隔 10 秒去监控面板的最左边点一下记录按钮,观察 3 次的内存占用情况:
上图,发现 3 次的内存占用几乎差不多,可以确定,首页没问题,并且不是整个应用的问题,那继续用上面的步骤 DDOS 其他页面
发现问题
在压测其中一个页面的时候,观察到内存不断飙涨,如下图,74->95->115,可以确定就是这个页面了:
点击一个快照,中间上面的过滤选择 comparison 比较,目标是上一个快照。右边的数据 delta 进行降序,delta 意思是增量:
在上表第一名增量的详情里,我一眼就发现了 share-total
字段,因为在这个 h5 页面,使用了ahook的 useRequest 进行了 cacheKey 缓存,这个 cacheKey 就是 share-total
,印象比较深
解决问题
然后想到了之前在别的组,好像有同事也发现了 useRequest 的泄漏问题,那看来官方还是没修,既然不修也等不了了,可以基于他封装个 useRequestV2
因为 useRequest 会在 server 阶段执行,在这个阶段把 cacheKey 置空就好,封装代码比较多,下面是部分代码:
const useReqRes = useRequest<TData, TParams>(api, {
...options,
...getStorageCacheOptions(options),
cacheKey: isServer() ? undefined : options?.cacheKey,
})
改完后,本地测试,内存不再变化,直接上线
上线一天后,观察 grafana,一切正常,修复后的 24 小时内存趋势:
修复后的 7 天内存趋势:
非常健康,估计 5 年都不需要重启