首页 > 解决方案 > 在谷歌云功能中运行节点js导出

问题描述

我们需要导出一个 zip 文件,其中包含大量数据(几 GB)。zip 存档需要包含大约 50-100 个 indesign 文件(每个大约 100mb)和一些其他较小的文件。我们尝试使用谷歌云功能来实现它(成本更低等)。该功能通过配置文件触发,该文件上传到存储桶中。配置文件包含需要将哪些文件放入 zip 中的所有信息。不幸的是,总是达到 2gb 的内存限制,所以函数永远不会成功。

我们尝试了不同的方法:第一个解决方案是循环文件,创建 Promise 以下载它们,在循环完成后,我们尝试一次解决所有 Promise。(文件通过流直接下载到文件中)。第二次尝试是等待 for 循环内的每次下载,但再次达到内存限制。

所以我的问题是:为什么 node js 不清除流?似乎节点将每个流文件保存在内存中并最终崩溃。我已经尝试按照此处的建议将 readStream 和 writeStream 设置为 null:

如何防止 node.js 中的内存泄漏?

但没有变化。

注意:我们从来没有达到这一点,所有文件都被下载以创建 zip 文件。在第一个文件之后它总是失败。

请参阅下面的代码片段:

// first try via promises all:
const promises = []
for (const file of files) {
    promises.push(downloadIndesignToExternal(file, 'xxx', dir));
}

await Promise.all(promises)


// second try via await every step (not performant in terms of execution time, but we wanted to know if memory limit is also reached:
for (const file of files) {
    await downloadIndesignToExternal(file, 'xxx', dir);
}


// code to download indesign file
function downloadIndesignToExternal(activeId, externalId, dir) {
  return new Promise((resolve, reject) => {
    let readStream = storage.bucket(INDESIGN_BUCKET).file(`${activeId}.indd`).createReadStream()
    let writeStream = fs.createWriteStream(`${dir}/${externalId}.indd`);
    readStream.pipe(writeStream);
    writeStream.on('finish', () => {
      resolve();
    });
    writeStream.on('error', (err) => {
      reject('Could not write file');
    })
  })
}

标签: node.jsgarbage-collectiongoogle-cloud-functionsgoogle-cloud-storage

解决方案


重要的是要知道 /tmp (os.tmpdir()) 是Cloud Functions 中基于内存的文件系统。当您将文件下载到 /tmp 时,它会占用内存,就像您将文件保存到内存中的缓冲区一样。

如果您的函数需要的内存超出了可以为函数配置的内存,那么 Cloud Functions 可能不是解决此问题的最佳方案。

如果您仍想使用 Cloud Functions,则必须找到一种将输入文件直接流式传输到输出文件的方法,但无需在函数中保存任何中间状态。我确信这是可能的,但您可能需要为此编写大量额外的代码。


推荐阅读