Node.js中@aws-sdk/client-s3内存泄漏警告问题求助
解决@aws-sdk/client-s3的MaxListenersExceededWarning内存泄漏警告
问题根源
你当前的代码在每次调用s3Upload和s3DownloadFile时都会创建新的S3Client实例。每个S3Client会初始化独立的HTTP连接池,多次调用后会累积大量事件监听器,超出Node.js默认的监听器数量限制,从而触发MaxListenersExceededWarning。
解决方案
1. 复用S3Client实例
将S3Client实例的创建移到模块顶部,整个应用复用同一个客户端实例,避免重复创建连接和监听器。
修改后的s3Service.js代码:
const { S3Client, PutObjectCommand, GetObjectCommand, } = require("@aws-sdk/client-s3"); // 全局复用一个S3Client实例 const s3client = new S3Client({ region: process.env.AWS_REGION }); exports.s3Upload = async (file, fileKey) => { const param = { Bucket: process.env.AWS_BUCKET_NAME, Key: fileKey, Body: file.buffer, }; return s3client.send(new PutObjectCommand(param)); }; exports.s3DownloadFile = async (fileKey) => { const param = { Bucket: process.env.AWS_BUCKET_NAME, Key: fileKey, }; try { return await s3client.send(new GetObjectCommand(param)); } catch (error) { console.error("Error", error); } };
2. 优化下载流的处理
在下载路由中,确保流在结束或出错时正确清理,避免潜在的资源泄漏:
修改后的路由代码:
/** Import statements */ const express = require("express"); const { s3DownloadFile } = require("../s3Service"); const { Company } = require("../models/companies"); const { Backup } = require("../models/backups"); const router = express.Router(); router.get("/download/:companyName/latest", async (req, res) => { try { const company = await Company.findOne({ where: { companyName: req.params.companyName, }, }); if (!company) return res.status(404).send("The specified company does not exist"); const backups = await Backup.findAll({ where: { companyID: company.dataValues.companyID, }, order: [["createdAt", "DESC"]], }); if (backups.length === 0) return res.status(404).send("There are no backups for this company"); let fileKey = `${company.companyID}/${backups[0].dataValues.fileName}`; const data = await s3DownloadFile(fileKey); // Set appropriate headers for the file download res.attachment(fileKey); // 处理流的错误和结束事件,确保资源释放 data.Body.pipe(res); data.Body.on("error", (err) => { if (!res.headersSent) { res.status(500).send("File download failed"); } }); res.on("finish", () => { data.Body.destroy(); }); } catch (error) { res.status(500).send(error.message); } }); module.exports = router;
3. 临时调整监听器上限(不推荐)
如果上述方案无法立即生效,可以临时提高事件监听器的上限,但这只是治标不治本的方法:
在应用入口文件顶部添加:
require('events').EventEmitter.defaultMaxListeners = 20;
内容的提问来源于stack exchange,提问作者Ashraf Lobo
相关产品推荐
相关产品推荐

