【问题标题】:GAE Python LXML - Exceeded soft private memory limitGAE Python LXML - 超过软私有内存限制
【发布时间】:2013-02-08 01:36:55
【问题描述】:

我正在获取 GZipped LXML 文件并尝试将产品条目写入数据库模型。以前我遇到了本地内存问题,通过 SO (question) 的帮助解决了这些问题。现在我一切正常并部署了它,但是在服务器上我收到以下错误:

Exceeded soft private memory limit with 158.164 MB after servicing 0 requests total

现在我尽我所能减少内存使用,目前正在使用下面的代码。 GZipped 文件约为 7 MB,而解压缩后为 80 MB。在本地,代码运行良好。我尝试将它作为 HTTP 请求以及 Cron Job 运行,但没有任何区别。现在我想知道是否有任何方法可以提高效率。

关于 SO 的一些类似问题参考了我不熟悉的前端和后端规范。我正在运行 GAE 的免费版本,这个任务必须每周运行一次。任何关于前进的最佳方式的建议将不胜感激。

from google.appengine.api.urlfetch import fetch
import gzip, base64, StringIO, datetime, webapp2
from lxml import etree
from google.appengine.ext import db

class GetProductCatalog(webapp2.RequestHandler):
  def get(self):
    user = XXX
    password = YYY
    url = 'URL'

    # fetch gziped file
    catalogResponse = fetch(url, headers={
        "Authorization": "Basic %s" % base64.b64encode(user + ':' + password)
    }, deadline=10000000)

    # the response content is in catalogResponse.content
    # un gzip the file
    f = StringIO.StringIO(catalogResponse.content)
    c = gzip.GzipFile(fileobj=f)
    content = c.read()

    # create something readable by lxml
    xml = StringIO.StringIO(content)

    # delete unnecesary variables
    del f
    del c
    del content

    # parse the file
    tree = etree.iterparse(xml, tag='product')

    for event, element in tree:
        if element.findtext('manufacturer') == 'New York':
            if Product.get_by_key_name(element.findtext('sku')):
                    coupon = Product.get_by_key_name(element.findtext('sku'))
                    if coupon.last_update_prov != datetime.datetime.strptime(element.findtext('lastupdated'), "%d/%m/%Y"):
                        coupon.restaurant_name = element.findtext('name')
                        coupon.restaurant_id = ''
                        coupon.address_street = element.findtext('keywords').split(',')[0]
                        coupon.address_city = element.findtext('manufacturer')
                        coupon.address_state = element.findtext('publisher')
                        coupon.address_zip = element.findtext('manufacturerid')
                        coupon.value = '$' + element.findtext('price') + ' for $' + element.findtext('retailprice')
                        coupon.restrictions = element.findtext('warranty')
                        coupon.url = element.findtext('buyurl')
                        if element.findtext('instock') == 'YES':
                            coupon.active = True
                        else:
                            coupon.active = False
                        coupon.last_update_prov = datetime.datetime.strptime(element.findtext('lastupdated'), "%d/%m/%Y")
                        coupon.put()
                    else:
                        pass
            else:
                    coupon = Product(key_name = element.findtext('sku'))
                    coupon.restaurant_name = element.findtext('name')
                    coupon.restaurant_id = ''
                    coupon.address_street = element.findtext('keywords').split(',')[0]
                    coupon.address_city = element.findtext('manufacturer')
                    coupon.address_state = element.findtext('publisher')
                    coupon.address_zip = element.findtext('manufacturerid')
                    coupon.value = '$' + element.findtext('price') + ' for $' + element.findtext('retailprice')
                    coupon.restrictions = element.findtext('warranty')
                    coupon.url = element.findtext('buyurl')
                    if element.findtext('instock') == 'YES':
                        coupon.active = True
                    else:
                        coupon.active = False

                    coupon.last_update_prov = datetime.datetime.strptime(element.findtext('lastupdated'), "%d/%m/%Y")
                    coupon.put()
        else:
            pass

        element.clear()

UDPATE

根据 Paul 的建议,我实施了后端。经过一些麻烦后,它就像一个魅力 - 找到我在下面使用的代码。

我的 backends.yaml 如下所示:

backends:
- name: mybackend
  instances: 10
  start: mybackend.app
  options: dynamic

而我的app.yaml如下:

handlers:
- url: /update/mybackend
  script: mybackend.app
  login: admin

【问题讨论】:

  • 您多久需要从 xml 文件中导入数据。如果只是偶尔您会发现使用 remote_api 并在本地处理文件并直接写入数据存储更容易。然后完整的可能与您本地机器可以处理的一样大。
  • 还要注意 del c 可能不会做任何事情,除非你明确调用 gc.collect() 因为这些东西可能在很长一段时间内都不会被收集。还要看看你的代码,你有读取文件/StringIO、xml(这是 c 的 StringIO 包装版本),然后是完整的解析树。你说未压缩它是 80MB,加上你至少有一个你没有 gc'd 的副本加上树。您可能会考虑使用拉解析策略,这意味着您在内存中没有完整的解析树副本以及字符串。
  • 感谢蒂姆的意见。是的,我确实考虑了 remote_api 选项,但在某些时候,此脚本将以每日速度运行,这就是我选择当前设置的原因。我将研究您对拉解析策略的建议,看看它是否可以提高性能。再次感谢!

标签: python google-app-engine memory lxml


【解决方案1】:

后端类似于前端实例,但它们无法扩展,您必须根据需要停止和启动它们(或将它们设置为动态,这可能是您最好的选择)。

您可以在后端拥有多达 1024MB 的内存,因此它可能适合您的任务。

https://developers.google.com/appengine/docs/python/backends/overview

App Engine 后端是您的应用程序实例,它们不受请求截止日期的影响,并且可以访问比普通实例更多的内存(最高 1GB)和 CPU(最高 4.8GHz)。它们专为需要更快性能、大量可寻址内存以及连续或长时间运行的后台进程的应用程序而设计。后端有多种尺寸和配置,并按正常运行时间而不是 CPU 使用情况计费。

后端可以配置为驻留或动态。居民 后端连续运行,允许您依赖它们的状态 随着时间的推移内存并执行复杂的初始化。动态后端 当他们收到请求并被拒绝时开始存在 空闲时;它们非常适合间歇性工作或由 用户活动。有关之间差异的更多信息 常驻和动态后端,请参阅后端类型以及 讨论启动和关闭。

听起来正是您所需要的。免费使用级别也适合您的任务。

【讨论】:

  • 非常感谢保罗。我会尝试一下,一旦我有它会发布反馈。
  • 所以我尝试实现它,但它无法识别 backends.yaml。我已经更新了问题描述。我错过了一些明显的东西吗?今天晚些时候我会做更多的测试。感谢您的帮助!
  • 那完全是另一个问题。建议您开始一个不同的问题和/或检查一些类似的问题。例如。你开始后端了吗?
  • 谢谢保罗。我不想用这样一个小问题来稀释 SO,但猜你是对的。感谢您的帮助!
【解决方案2】:

关于后端:查看您提供的示例 - 似乎您的请求只是由前端实例处理。

要让它由后端处理,请尝试调用如下任务:http://mybackend.my_app_app_id.appspot.com/update/mybackend

另外,我认为您可以从您的backends.yaml 中删除:start: mybackend.app

【讨论】:

  • 感谢您的建议。原来我必须通过“appcfg backends update [backend]” 启动后端
  • 你是对的,常规部署不影响后端,后端基本上被视为应用的单独版本。
猜你喜欢
  • 1970-01-01
  • 2012-03-09
  • 2012-04-24
  • 2018-12-29
  • 2014-10-31
  • 2016-01-04
  • 2022-01-15
  • 2020-09-03
相关资源
最近更新 更多