【发布时间】:2020-03-18 01:16:51
【问题描述】:
所以这篇文章是一个由两部分组成的问题。 我正在使用实例类 F4 测试 Google 应用引擎(内存:1024 MB 和 CPU 2.4GHz,具有自动缩放功能)。这是在 App 引擎上找到的配置文件:
runtime: nodejs10
env: standard
instance_class: F4
handlers:
- url: .*
script: auto
automatic_scaling:
min_idle_instances: automatic
max_idle_instances: automatic
min_pending_latency: automatic
max_pending_latency: automatic
network: {}
我的 Nodejs 服务器具有正常的 Post 和 Get 路由,除了提供静态文件和从 MongoDB 获取数据之外,这些路由不会繁重。这是使用这些路由时的内存使用图:
图 1. 显示从这些非重路由导航时在 Google StackDriver 上看到的内存使用情况
我的第一个问题是,为什么这么小的任务需要这么多内存(大约 500mb)?
我正在使用 Chrome Devtools Node.js Memory Profiler 在我的 Windows 10 机器上运行分析测试,它提供的数据远不及这些数字,我在获取堆快照时获得了大约 52MB。
我的第二个问题是关于获取上传文件并使用Sharp将其调整为3张不同图像的特定路线。
尽管在我的 Windows 10 机器上按预期工作,但为什么调整大小完成后内存没有被释放?
我正在对 10mb 图像进行大小调整测试,该图像被转换为 缓冲区,然后存储在内存中强>。 转换代码如下:
const resizeAndSave = (buffer, filePath, widthResize, transform) => {
const { width, height, x, y } = transform;
//create sharp instance
const image = sharp(buffer);
//prepare to upload to Cloud storage
const file = publicBucket.file(filePath);
return new Promise((resolve, reject) => {
image
.metadata()
.then(metadata => {
//do image operations
const img = image
.extract({
width: isEmpty(transform) ? metadata.width : parseInt(width),
height: isEmpty(transform) ? metadata.height : parseInt(height),
left: isEmpty(transform) ? 0 : parseInt(x),
top: isEmpty(transform) ? 0 : parseInt(y)
})
.resize(
metadata.width > widthResize
? {
width: widthResize || metadata.width,
withoutEnlargement: true
}
: metadata.height > widthResize
? {
height: widthResize || metadata.height,
withoutEnlargement: true
}
: {
width: widthResize || metadata.width,
withoutEnlargement: true
}
)
.jpeg({
quality: 40
});
//pipe to cloud storage and resolve filepath when done
img
.pipe(file.createWriteStream({ gzip: true }))
.on("error", function(err) {
reject(err);
})
.on("finish", function() {
// The file upload is complete.
resolve(filePath);
});
})
.catch(err => {
reject(err);
});
});
};
这个函数每张图片串联调用3次,为了测试,没有使用Promise.all来阻止它们并行运行:
async function createAvatar(identifier, buffer, transform, done) {
const dir = `uploads/${identifier}/avatar`;
const imageId = nanoid_(20);
const thumbName = `thumb_${imageId}.jpeg`;
await resizeAndSave(buffer, `${dir}/${thumbName}`, 100, transform);
const mediumName = `medium_${imageId}.jpeg`;
await resizeAndSave(buffer, `${dir}/${mediumName}`, 400, transform);
const originalName = `original_${imageId}.jpeg`;
await resizeAndSave(buffer, `${dir}/${originalName}`, 1080, {});
done(null, {
thumbUrl: `https://bucket.storage.googleapis.com/${dir_}/${thumbName}`,
mediumUrl: `https://bucket.storage.googleapis.com/${dir_}/${mediumName}`,
originalUrl: `https://bucket.storage.googleapis.com/${dir_}/${originalName}`
});
/* Promise.all([thumb])
.then(values => {
done(null, {
thumbUrl: `https://bucket.storage.googleapis.com/${dir_}/${thumbName}`,
mediumUrl: `https://bucket.storage.googleapis.com/${dir_}/${mediumName}`,
originalUrl: `https://bucket.storage.googleapis.com/${dir_}/${originalName}`
});
})
.catch(err => {
done(err, null);
}); */
}
在我的 Window 10 机器上运行服务器时的堆快照是:
图 2.当导航到图像大小调整路线一次时,来自 Chrome devtools for Node.js 的堆快照
这些堆快照清楚地表明,用于在内存中存储 10mb 图像并调整其大小的内存正在返回到我机器上的操作系统。
StackDriver 上报告的内存使用情况为: 图 3.Google StackDriver 导航到一次调整图像大小路径时的内存使用情况
这清楚地表明,操作完成时内存并没有被释放,而且非常高,从晚上 8 点左右开始,它上升到 800mb 并且从未下降。
我也尝试过 Stackdriver 分析器,但它没有显示任何高内存使用情况,但实际上,它显示大约 55mb,接近我的 Windows 机器:
所以如果我的分析是正确的,我假设它与运行应用引擎中的实例的操作系统有关?我不知道。
更新:这是在使用图像处理路线并且一个小时未接触应用后从 Stackdriver 获取的最新内存使用情况:
图 5. 导航到图像调整大小路径后,让应用闲置一小时时的内存使用情况
更新 2:根据 Travis 的建议,我在导航到路由时查看了进程,发现内存使用率略高于堆,但与应用引擎显示的相差甚远: 图 6.Windows 10 在 image processign 运行时处理 Nodejs 内存
Update 3:在与图 5 相同的时间间隔内使用的 Instance 数量(内存使用率很高): 图7.与图5在同一时间间隔内使用的实例数
更新 4: 所以我尝试切换到实例类 F1(256 MB 600 MHz)看看会发生什么。结果显示空闲时内存使用量减少,但当我处理 10mb 图像时,应用程序引擎会发送一条警告说升级内存。 (它显示了 2 个正在运行的实例)。 Fig 8.F1 instance class with 256MB ram when app is idle
这让我认为这些实例无论如何都试图占用大部分可用内存。
【问题讨论】:
-
请记住,堆并不是程序分配的唯一内存类型...我建议在您的窗口上查看整个进程的内存使用情况,以及它产生的任何进程机器,而不仅仅是堆大小。我怀疑它看起来会与您在应用引擎上看到的相似。
-
您检查流程是对的,但它仍然与应用引擎显示的内容相去甚远。请查看更新后的问题。
-
你在 node 上使用什么图片库?通常那些包括产生额外的图像处理进程的二进制文件。我的意思是你在比较苹果和橘子。了解程序的总内存消耗比仅查看堆大小要复杂得多。事实上,您已经注意到两个平台上的堆大小是相同的......
-
正如我所提到的,我正在使用Sharp。问题是为什么它在应用引擎上比在 Windows 10 上需要更多的内存。
-
除了运行图像处理之外,在应用程序空闲时不断使用 500mb 的内存对我来说似乎遥不可及。
标签: javascript node.js google-app-engine google-cloud-platform memory-leaks