Node.js多URL循环请求内存泄漏问题求助及代码排查
内存泄漏问题分析与优化方案
嘿,我帮你排查下这个Node.js脚本的内存泄漏问题,然后给出具体的优化思路和代码!
先说说为啥会内存泄漏
你的脚本主要有这几个核心问题导致内存飙升甚至宕机:
- 无限制并发请求:每次
remoteControllerPress执行都会一次性发起所有URL的请求,要是前一轮的请求还没处理完,6秒后又会发起新的一批,请求越堆越多,大量未释放的HTTP连接、回调上下文占满内存。 - 用了废弃的
request库:request早就停止维护了,本身就存在不少内存泄漏的已知问题,而且对资源的自动清理做得不好。 - 异步逻辑没闭环:递归式的
setTimeout调用,加上没等前一轮所有请求完成就发起下一轮,导致内存里累积大量未完成的异步任务上下文。 - 错误处理不完整:部分请求失败的分支没做资源清理,可能残留句柄占用内存。
具体优化步骤&代码
下面是优化后的完整方案,我会一步步说明改动点:
1. 替换依赖,用现代HTTP客户端+并发控制
把request换成node-fetch(原生Fetch的Node.js实现,更稳定),再用p-limit限制并发请求数,避免请求堆积。先装依赖:
npm install node-fetch p-limit
2. 重构异步逻辑,确保任务闭环
改用Promise版的fs模块,处理异步操作更清晰;等一轮所有请求都完成后,再发起下一轮调用,避免任务堆积;用path模块处理路径更安全,不会因为拼接出错导致问题。
优化后的完整代码
const fs = require('fs').promises; // 用Promise版fs,异步处理更可控 const path = require('path'); // 安全处理路径拼接 const fetch = require('node-fetch'); const dateTime = require('node-datetime'); const express = require('express'); const pLimit = require('p-limit'); const app = express(); const urls = [ 'http://www.google.com', 'https://www.bing.com', 'http://www.yahoo.com', 'http://stackoverflow.com', 'http://github.com', 'http://www.yahoo.co.jp' ]; const concurrencyLimit = 2; // 限制同时跑2个请求,可根据服务器配置调整 const limit = pLimit(concurrencyLimit); async function remoteControllerPress() { const dt = dateTime.create(); const newCurrentTimeDir = dt.format('Y-m-d-H-M-S').toString(); const dirName = path.join(__dirname, newCurrentTimeDir); // 用path.join避免路径错误 // 检查目录是否存在,不存在则创建 try { await fs.access(dirName); } catch { await fs.mkdir(dirName, { recursive: true }); // 递归创建,即使父目录不存在也能成 } console.log(`==============calling at ${newCurrentTimeDir}\n`); // 用p-limit包装请求,限制并发数 const requestTasks = urls.map(async (url, index) => { return limit(async () => { try { const response = await fetch(url); console.log(`---------URL: ${url}`); if (response.ok) { // 用response.ok判断请求成功,比直接判断200更全面 const body = await response.text(); console.log(`URL: ${url} statusCode: ${response.status}`); const filePath = path.join(dirName, `file_${index}`); await fs.writeFile(filePath, body); console.log(`${filePath} was saved!\n\n`); } else { console.log(`URL: ${url} failed with status: ${response.status}`); } } catch (error) { console.log(`URL: ${url} error:`, error.code || error.message); } }); }); // 等所有请求都处理完,再发起下一轮 await Promise.all(requestTasks); setTimeout(remoteControllerPress, 6000); } console.log("==============running...\n "); remoteControllerPress(); app.post('/', function (req, res) { res.send('hello world'); }); app.listen(3000, () => console.log('Server start listening on port 3000...'));
额外的内存优化小建议
- 定期清理旧目录:要是脚本长期运行,生成的目录会越来越多,不仅占磁盘,还可能残留未释放的文件句柄,可以加个定时任务,比如删掉7天前的旧目录。
- 监控内存状态:用
node --inspect启动脚本,然后用Chrome DevTools的Memory面板拍内存快照,就能精准定位剩下的内存泄漏点。 - 调整并发数:根据你的服务器配置,调整
concurrencyLimit的值,比如服务器性能好可以调到3-4,性能一般就保持1-2,平衡速度和内存占用。
内容的提问来源于stack exchange,提问作者munyso
相关产品推荐
相关产品推荐

