运行 google 云函数中的节点 js 导出
Running node js export in google cloud function
我们需要导出一个 zip 文件,其中包含大量数据(几 GB)。 zip 存档需要包含大约 50-100 个 indesign 文件(每个大约 100mb)和一些其他较小的文件。我们尝试使用 google 云函数来实现它(成本更低等)。该函数是通过配置文件触发的,该配置文件被上传到存储桶中。配置文件包含需要将哪些文件放入 zip 中的所有信息。不幸的是,总是会达到 2gb 的内存限制,因此该函数永远不会成功。
我们尝试了不同的方法:
第一个解决方案是遍历文件,创建下载它们的承诺,在循环完成后,我们尝试立即解决所有承诺。 (文件通过流直接下载到文件中)。
第二次尝试是在 for 循环内等待每次下载,但再次达到内存限制。
所以我的问题是:
为什么节点js不清除流?似乎节点将每个流式文件保存在内存中并最终崩溃。我已经尝试按照此处的建议将 readStream 和 writeStream 设置为 null:
How to prevent memory leaks in 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');
})
})
}
重要的是要知道 /tmp (os.tmpdir()) 是一个 memory-based filesystem in Cloud Functions。当您将文件下载到 /tmp 时,它会占用内存,就像您将它保存到缓冲区中的内存一样。
如果您的函数需要的内存超过可以为函数配置的内存,那么 Cloud Functions 可能不是解决此问题的最佳方案。
如果您仍想使用 Cloud Functions,则必须找到一种方法将输入文件直接流式传输到输出文件,但不在函数中保存任何中间状态。我相信这是可能的,但您可能需要为此编写大量额外代码。
任何感兴趣的人:
我们通过将文件流式传输到 zip 并将其直接流式传输到 google 云存储来让它工作。内存使用量现在约为 150-300mb,因此这对我们来说非常有效。
我们需要导出一个 zip 文件,其中包含大量数据(几 GB)。 zip 存档需要包含大约 50-100 个 indesign 文件(每个大约 100mb)和一些其他较小的文件。我们尝试使用 google 云函数来实现它(成本更低等)。该函数是通过配置文件触发的,该配置文件被上传到存储桶中。配置文件包含需要将哪些文件放入 zip 中的所有信息。不幸的是,总是会达到 2gb 的内存限制,因此该函数永远不会成功。
我们尝试了不同的方法: 第一个解决方案是遍历文件,创建下载它们的承诺,在循环完成后,我们尝试立即解决所有承诺。 (文件通过流直接下载到文件中)。 第二次尝试是在 for 循环内等待每次下载,但再次达到内存限制。
所以我的问题是: 为什么节点js不清除流?似乎节点将每个流式文件保存在内存中并最终崩溃。我已经尝试按照此处的建议将 readStream 和 writeStream 设置为 null:
How to prevent memory leaks in 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');
})
})
}
重要的是要知道 /tmp (os.tmpdir()) 是一个 memory-based filesystem in Cloud Functions。当您将文件下载到 /tmp 时,它会占用内存,就像您将它保存到缓冲区中的内存一样。
如果您的函数需要的内存超过可以为函数配置的内存,那么 Cloud Functions 可能不是解决此问题的最佳方案。
如果您仍想使用 Cloud Functions,则必须找到一种方法将输入文件直接流式传输到输出文件,但不在函数中保存任何中间状态。我相信这是可能的,但您可能需要为此编写大量额外代码。
任何感兴趣的人: 我们通过将文件流式传输到 zip 并将其直接流式传输到 google 云存储来让它工作。内存使用量现在约为 150-300mb,因此这对我们来说非常有效。