【问题标题】:Memory leak in reading files from Google Cloud Storage at Google App Engine (python)在 Google App Engine (python) 从 Google Cloud Storage 读取文件时出现内存泄漏
【发布时间】:2015-09-26 06:09:54
【问题描述】:

以下是在 Google App Engine 上运行的 Python 代码的一部分。 它使用 cloudstorage 客户端从 Google Cloud Storage 获取文件。

问题是每次代码读取一个大文件(约10M),实例使用的内存会线性增加。很快,由于“在服务 40 个请求后,超过了 128 MB 和 134 MB 的软专用内存限制”,该进程被终止。

class ReadGSFile(webapp2.RequestHandler):
    def get(self):
        import cloudstorage as gcs

        self.response.headers['Content-Type'] = "file type"
        read_path = "path/to/file"

        with gcs.open(read_path, 'r') as fp:
            buf = fp.read(1000000)
            while buf:
                self.response.out.write(buf)
                buf = fp.read(1000000)
            fp.close()

如果我注释掉以下行,那么实例中的内存使用情况会发生变化。所以应该是webapp2的问题。

  self.response.out.write(buf)

假设webapp2响应完成后会释放内存空间。但在我的代码中,它没有。

【问题讨论】:

  • 响应对象只能写入一次。
  • 输出流缓冲内存中的所有输出,然后在处理程序退出时发送最终输出。 webapp 不支持将数据流式传输到客户端。您需要用于流式传输的频道 API。
  • 我只尝试写入响应对象一次:“buf = fp.read(20000000); self.response.out.write(buf); fp.close();”。但这并不能解决问题

标签: python google-app-engine memory-leaks google-cloud-storage webapp2


【解决方案1】:

根据上述用户voscausa的评论建议,我改变了文件下载的方案,即使用Blobstore服务文件下载。现在内存泄漏的问题解决了。

参考:https://cloud.google.com/appengine/docs/python/blobstore/#Python_Using_the_Blobstore_API_with_Google_Cloud_Storage

from google.appengine.ext import blobstore
from google.appengine.ext.webapp import blobstore_handlers

class GCSServingHandler(blobstore_handlers.BlobstoreDownloadHandler):
  def get(self):
    read_path = "/path/to/gcs file/"  # The leading chars should not be "/gs/"
    blob_key  = blobstore.create_gs_key("/gs/" + read_path)

    f_name = "file name"
    f_type = "file type" # Such as 'text/plain'

    self.response.headers['Content-Type'] = f_type
    self.response.headers['Content-Disposition'] = "attachment; filename=\"%s\";"%f_name
    self.response.headers['Content-Disposition'] += " filename*=utf-8''" + urllib2.quote(f_name.encode("utf8"))

    self.send_blob(blob_key)

【讨论】:

  • 谢谢 - 这救了我 - 遇到了同样的问题 - GCS 访问内存泄漏!
【解决方案2】:

尝试清除上下文缓存。

from google.appengine.ext import ndb

context = ndb.get_context()
context.clear_cache()

See documentation here

通过在后台任务中执行长时间运行的查询,可以 上下文缓存消耗大量内存。这是 因为缓存保留了检索到的每个实体的副本,或者 存储在当前上下文中。为了避免内存异常 长时间运行的任务,您可以禁用缓存或设置策略 排除占用内存最多的实体。

您也可以尝试清除 webapp2 响应对象缓冲区。在while循环

之前插入这行代码
self.response.clear()

响应将所有输出缓冲在内存中,然后发送最终输出 当处理程序退出时。 webapp2 不支持将数据流式传输到 客户。 clear() 方法擦除输出缓冲区的内容, 留空。

Check this link

【讨论】:

  • 我添加了缓存清除代码,但它不起作用。顺便说一句,在我的应用程序中,我使用 db 而不是 ndb 来创建模型。因此,似乎问题本身与 ndb 缓存无关。
  • 我已经测试了清除response缓冲区的方法,但未能解决问题。即在while循环中将数据写入缓冲区之前调用“self.response.clear()”。但是没有释放响应的内存缓冲区。看来响应中的缓存机制与数据缓冲不同。
【解决方案3】:

我遇到过类似的问题。在我的代码中,我按顺序下载了不少 1-10MB 的文件,对所有文件进行一些处理,然后将结果发布到云端。

我目睹了严重的内存泄漏,无法连续处理超过 50-100 次下载。

不愿将下载代码重写到 Blobstore 我尝试了最后的实验,每次下载后手动调用垃圾收集:

import gc
gc.collect()

我现在运行代码几分钟,没有任何“超过软私有内存限制”,并且实例的内存占用似乎以更慢的速度增加。

显然,这可能只是运气好,足迹仍在逐渐增加,但有所下降,并且该实例已经服务了 2000 个请求。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-07-22
    • 2013-01-26
    • 2021-01-02
    • 2020-03-24
    • 1970-01-01
    • 1970-01-01
    • 2019-04-02
    相关资源
    最近更新 更多