【问题标题】:NodeJS Error When Downloading Multiple SFTP Files "Possible EventEmitter memory leak detected. 11 error listeners added"下载多个 SFTP 文件时出现 NodeJS 错误“检测到可能的 EventEmitter 内存泄漏。添加了 11 个错误侦听器”
【发布时间】:2018-10-15 21:37:27
【问题描述】:

使用 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

【问题讨论】:

  • 你有没有想过这个问题?我也有同样的问题。
  • 我认为这是 'ssh2-sftp-client' 软件包的问题。我切换到只使用“ssh2”,我不再遇到这个问题。
  • 针对这个问题运行了 2 天。写了一个答案here
  • 检查这个related anser我遇到了同样的问题

标签: javascript node.js sftp node-streams


【解决方案1】:

所以您说您正在尝试在 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();
});

【讨论】:

  • 给你两个问题。一个是我尝试运行您的代码,它似乎进入了递归函数内部的无限循环并引发了一大堆错误。二,即使使用排队系统,我是否仍会遇到最大侦听器问题?在我的 OP 中,我基本上有一个 1 的排队系统,它应该在开始第二个 1 之前等待每个 1 下载。我觉得即使有一个 10 的队列,一旦我在第二个队列中到达第 11 个,我会获取最大侦听器错误。
  • 我只是看了看,发现缺少完整性检查(如果字符串为空,则可能会触发错误)。还添加了另一项检查以确保递归函数不会发送要处理的空字符串。至于你的第二个问题,我在你的承诺之前错过了await,所以我认为它同时产生了几个听众,但如果是这种情况,我无法确认另一种方式。所以你是对的,这是相同的逻辑。除非对 sftp.get 的调用产生新的侦听器,直到调用 sftp.end() 才会被拆除,否则我不确定是什么原因造成的。
猜你喜欢
  • 2013-03-12
  • 2016-10-08
  • 1970-01-01
  • 2021-03-13
  • 2022-10-21
  • 2021-07-17
  • 1970-01-01
  • 2021-09-05
相关资源
最近更新 更多