繁体   English   中英

为什么 Google App 引擎不使用 Node.js 释放 memory

[英]Why isn't Google App engine freeing memory using Node.js

所以这篇文章是一个两部分的问题。 我正在使用实例 class 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 路由,除了提供 static 文件和从 MongoDB 获取数据之外,这些路由不会繁重。 这是使用这些路由时的 memory 使用图:

图 1.Graph 显示了从这些非重路由获取时在 Google StackDriver 上看到的内存使用情况

我的第一个问题是,为什么这么小的任务需要这么多内存(大约 500mb)?

我正在使用 Chrome Devtools Node.js Memory Profiler 在我的 Windows 10 机器上运行分析测试,并且它在获取这些数字时无处可取,我正在四处拍摄快照。

我的第二个问题是关于一个特定的路线,该路线采用上传的文件并使用sharp将其调整为3个不同的图像。

尽管在我的 Windows 10 机器上按预期工作,但在调整大小完成后,为什么 Memory 没有被释放?

我正在对10mb图像进行大小调整测试,该图像被转换为缓冲区,然后存储在 memory 中 转换代码如下:

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);
      });
  });
};

这个 function 对于每个图像被连续调用了 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.Node.js 的 chrome devtools 的堆快照

这些堆快照清楚地表明,使用 memory 将 10mb 图像存储在 memory 并调整其大小正在返回到我机器上的操作系统。

StackDriver 上报告的 memory 使用情况为: 图 3. 使用图像大小调整路由时 Google StackDriver 的内存使用情况

这清楚地表明memory在操作完成时并没有被释放,而且非常高,从晚上8点左右开始,它上升到800mb并且从未下降。

我也尝试了 Stackdriver 分析器,但它没有显示任何高 memory 使用率,但实际上,它显示大约 55mb,接近我的 windows 机器:

Fig 4.StackDriver profiler heap shot

因此,如果我的分析是正确的,我假设它与运行应用引擎中的实例的操作系统有关? 我没有任何线索。

更新:这是在使用图像处理路线并且一个小时不接触应用程序后从 Stackdriver 获得的最新 memory 用法:

图 5. 调整大小后让应用闲置一小时时的内存使用情况

更新 2 :根据 Travis 的建议,我在导航到路线时查看了该过程,发现 memory 的使用率略高于堆,但与应用引擎显示的相差甚远: 图 6.Windows 10 Nodejs 进程内存

更新 3 :在与图 5 相同的时间间隔内使用的实例数(而 memory 的使用率很高): 在与图 5 相同的时间间隔内使用的实例数

更新 4:所以我尝试切换到实例 class F1(256 MB 600 MHz)看看会发生什么。 结果显示空闲时内存使用量减少,但当我处理 10mb 图像时,应用程序引擎会发送一条警告说升级内存。 (它显示了 2 个正在运行的实例)。 具有 256MB 内存的 F1 实例类

这使我认为这些实例无论如何都试图占用大部分可用的内存。

因此,作为上述评论和您上次更新(更新 4)的结果,我们可以意识到:

  • Stackdriver Profiler 指标,显示平均 Memory 仅使用堆 Memory
  • 和 GCP 指标,它们展示了总 Memory 使用情况(所有实例)

是预期的行为。

调查 Node.js 应用程序 Memory 用法的一种方法是深入了解垃圾收集器的概念。

垃圾收集器(GC)的工作是回收未被使用的对象(垃圾)占用的 memory。

在这里这里您可以在 Node.js 中找到有关 Memory 管理和使用的信息

此外,Node.js 在认为有必要时会启动 GC。 根据这篇文章,检测可能的 memory 泄漏的想法是手动强制垃圾收集器并检查 memory 是否仍在上升。 然而,这不是一个好的做法,不建议在生产中使用,而只是作为一种诊断方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM