GAE Python LXML - 超过软私有内存限制
我正在获取一个经过GZ压缩的LXML文件,并试图将产品信息写入数据库模型。之前我遇到了本地内存的问题,后来在StackOverflow上得到了帮助(问题链接)。现在我已经把一切都搞定并部署了,但在服务器上却出现了以下错误:
Exceeded soft private memory limit with 158.164 MB after servicing 0 requests total
我尝试了所有我知道的方法来减少内存使用,目前使用的代码如下。这个GZ压缩文件大约7MB,而解压后是80MB。在本地运行时代码一切正常。我尝试过以HTTP请求和定时任务的方式运行,但没有什么区别。现在我在想有没有什么办法可以让它更高效。
在StackOverflow上,有一些类似的问题提到了前端和后端的配置,但我对此不太熟悉。我正在使用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()
更新
根据保罗的建议,我实现了后端。经过一些麻烦后,它终于顺利运行了——下面是我使用的代码。
我的backends.yaml文件如下:
backends:
- name: mybackend
instances: 10
start: mybackend.app
options: dynamic
而我的app.yaml文件如下:
handlers:
- url: /update/mybackend
script: mybackend.app
login: admin
2 个回答
关于后端:看你提供的例子,感觉你的请求只是由前端处理的。
要让它由后端处理,可以试着调用这个任务:http://mybackend.my_app_app_id.appspot.com/update/mybackend
另外,我觉得你可以把start: mybackend.app
这一行从你的backends.yaml
文件中去掉。
后端就像前端的实例,但它们不能自动扩展,你需要根据需要手动停止和启动它们(或者设置成动态的,这样可能是更好的选择)。
后端最多可以使用1024MB的内存,所以应该能满足你的需求。
https://developers.google.com/appengine/docs/python/backends/overview
App Engine的后端是你的应用程序的一种实例,它们不受请求时间限制,并且可以使用比普通实例更多的内存(最多1GB)和CPU(最多4.8GHz)。这些后端是为需要更快性能、大量可用内存和持续或长时间运行的后台进程而设计的。后端有多种大小和配置,计费是根据运行时间,而不是CPU使用量。
后端可以配置为常驻或动态。常驻后端会持续运行,这样你可以依赖它们的内存状态,并进行复杂的初始化。动态后端在收到请求时才会启动,闲置时会关闭;它们非常适合间歇性或由用户活动驱动的工作。想了解常驻和动态后端之间的区别,可以查看后端类型以及启动和关闭的讨论。
听起来这正是你需要的。免费的使用级别也适合你的任务。