javascript - 下载多个 SFTP 文件时出现 NodeJS 错误“检测到可能的 EventEmitter 内存泄漏。添加了 11 个错误侦听器”
问题描述
ssh2-sftp-client
使用库从 SFTP 站点下载多个文件时出现错误。抛出的错误似乎表明每次下载完成后节点流没有被清除。这导致我的应用程序出现内存泄漏。在生产中,我需要能够下载数千个文件,因此这种内存泄漏非常严重。如何关闭流以便在下载每个文件后释放内存?
代码:
const Client = require('ssh2-sftp-client');
const sftp = new Client();
sftp.connect({
host: '195.144.107.198',
port: 22,
username: 'demo',
password: 'password'
}).then(async () => {
const fileNames = ['readme.txt', 'readme.txt', 'readme.txt', 'readme.txt', 'readme.txt', 'readme.txt', 'readme.txt', 'readme.txt', 'readme.txt', 'readme.txt', 'readme.txt', 'readme.txt'];
// Loop through filenames
for (let i = 0; i < fileNames.length; i++) {
// Download all the files synchronously (1 at a time)
const fileName = fileNames[i];
await new Promise((resolve, reject) => { // <-- note the await
sftp.get(fileName, true, 'utf8').then((stream) => {
let text = '';
stream
.on('data', (d) => { text += d; })
.on('end', () => {
console.log('Success downloaded file', i);
resolve(text);
});
}).catch((err) => {
console.log('Error downloading file', err);
reject(err.message)
});
});
}
sftp.end();
});
注意:此代码使用公共 SFTP 站点,因此凭据不敏感,您可以运行它进行测试。在这里找到:https ://www.sftp.net/public-online-sftp-servers
错误(在文件 #9 下载后发生):
(node:44580) MaxListenersExceededWarning: Possible EventEmitter memory leak detected.
11 error listeners added. Use emitter.setMaxListeners() to increase limit
解决方案
因此,您说您正在尝试在 prod 中下载数千个文件,但您正在为每个文件使用一个侦听器。Node 仅允许您10
在触发警报之前创建最大的事件侦听器。
看:
https://nodejs.org/dist/latest-v8.x/docs/api/events.html#events_eventemitter_defaultmaxlisteners https://github.com/nodejs/help/issues/1051
如果你想纠正这个问题,我建议你实现一个queue
并且一次只下载 10 个文件。
就像是:
const Client = require('ssh2-sftp-client');
const sftp = new Client();
sftp.connect({
host: '195.144.107.198',
port: 22,
username: 'demo',
password: 'password'
}).then(async () => {
// Treat files array as a queue instead of an array
const fileQueue = ['readme.txt', 'readme.txt', 'readme.txt', 'readme.txt', 'readme.txt', 'readme.txt', 'readme.txt', 'readme.txt', 'readme.txt', 'readme.txt', 'readme.txt', 'readme.txt'];
// Use this function to grab files from your main files array
const downloadFilesFromQueue = (fileName) =>
new Promise((resolve, reject) => {
// Sanity check
if(!fileName) {
resolve();
}
sftp.get(fileName, true, 'utf8').then((stream) => {
let text = '';
stream
.on('data', (d) => { text += d; })
.on('end', () => {
console.log('Success downloaded file', fileName);
resolve(text);
});
}).catch((err) => {
console.log('Error downloading file', err);
reject(err.message);
});
})
// Handle errors
.catch((err) => console.log(err.message))
// Get next file from the queue
.then(() => {
// If there are no more items in the queue, we're done
if (!fileQueue.length) {
return;
}
downloadFilesFromQueue(fileQueue.shift())
});
// Track all unresolved promises
const unresolvedPromises = [];
// Request no more than 10 files at a time.
for (let i = 0; i < 10; i++) {
// Use file at the front of the queue
const fileName = fileQueue.shift();
unresolvedPromises.push(downloadFilesFromQueue(fileName));
}
// Wait until the queue is emptied and all file retrieval promises are
// resolved.
await Promise.all(unresolvedPromises);
// done
sftp.end();
});
推荐阅读
- c# - 继承自 Stephen Cleary 的 Nito.Disposables NuGet 包
- nearprotocol - 如何在near-sdk-rs中获取合约的near balance和token balance?
- java - 任何服务器、框架或 J2EE 本身在 Java 中的任何地方仍然使用序列化吗?
- html - 如何更改数据过滤器的 li:active 属性的颜色?
- python - 使用python将json文件保存到特定文件夹
- javascript - 没有与脚本语言 JavaScript 关联的脚本引擎
- php - laravel 8 fortify Auth::user() 登录后返回 null
- javascript - Chrome 扩展我需要使用哪种类型的脚本来处理请求?
- spring-boot - MongoTemplate.findAndModify 是否支持 OptimisticLockingException?
- python - 使用 SimpleITK 和 PyTorch 读取 dicom 文件的性能