Python中的S3备份内存使用情况
我现在使用WebFaction来做网站托管,选择的是基础套餐,提供80MB的内存。这个内存对我们目前的需求来说已经足够了,除了备份的部分。我们每天会把备份上传到S3。
备份的过程是这样的:先把数据库的数据导出,然后把所有文件打包成一个压缩文件,文件名里包含备份的日期,最后使用亚马逊提供的Python库把这个文件上传到S3。
不过,似乎(虽然我不太确定)是我的代码在读取文件时,或者是S3的代码在处理时,把整个文件都加载到了内存里。今天的备份文件大约有320MB,这样就占用了大约320MB的内存。这个情况导致WebFaction停止了我们所有的进程,结果备份没能完成,我们的网站也因此宕机了。
所以我想问:有没有办法不把整个文件都加载到内存里,或者有没有其他更好用的Python S3库,能更有效地使用内存?理想情况下,内存使用量最好控制在60MB以内!如果这做不到,我该如何把文件分割成几个部分,然后分别上传呢?
谢谢你的帮助。
这是我备份脚本中导致进程停止的代码部分:
filedata = open(filename, 'rb').read()
content_type = mimetypes.guess_type(filename)[0]
if not content_type:
content_type = 'text/plain'
print 'Uploading to S3...'
response = connection.put(BUCKET_NAME, 'daily/%s' % filename, S3.S3Object(filedata), {'x-amz-acl': 'public-read', 'Content-Type': content_type})
2 个回答
虽然有点晚了,但我也遇到过同样的问题,所以我来分享一下我的解决办法。
简单来说:在Python 2.6及以上版本中,可以!这是因为从2.6版本开始,httplib
支持类似文件的对象。所以你只需要...
fileobj = open(filename, 'rb')
content_type = mimetypes.guess_type(filename)[0]
if not content_type:
content_type = 'text/plain'
print 'Uploading to S3...'
response = connection.put(BUCKET_NAME, 'daily/%s' % filename, S3.S3Object(fileobj), {'x-amz-acl': 'public-read', 'Content-Type': content_type})
详细说说...
S3.py
这个库使用Python的httplib
来进行connection.put()
的HTTP请求。你可以在源代码中看到,它只是把data
参数传给了httplib
的连接。
在
def _make_request(self, method, bucket='', key='', query_args={}, headers={}, data='', metadata={}):
...
if (is_secure):
connection = httplib.HTTPSConnection(host)
else:
connection = httplib.HTTPConnection(host)
final_headers = merge_meta(headers, metadata);
# add auth header
self._add_aws_auth_header(final_headers, method, bucket, key, query_args)
connection.request(method, path, data, final_headers) # <-- IMPORTANT PART
resp = connection.getresponse()
if resp.status < 300 or resp.status >= 400:
return resp
# handle redirect
location = resp.getheader('location')
if not location:
return resp
...
如果我们看看Python httplib的文档,我们可以看到...
HTTPConnection.request(method, url[, body[, headers]])
这个方法会使用指定的HTTP请求方法和URL向服务器发送请求。如果有
body
参数,它应该是一个在头部结束后要发送的数据字符串。或者,它也可以是一个打开的文件对象,这样就会发送文件的内容;这个文件对象需要支持fileno()
和read()
方法。头部的Content-Length
会自动设置为正确的值。headers
参数应该是要与请求一起发送的额外HTTP头部的映射。在2.6版本中更改:body可以是一个文件对象。
不要把整个文件都读到你的filedata变量里。你可以使用一个循环,每次读取大约60MB的数据,然后再提交给亚马逊。
backup = open(filename, 'rb')
while True:
part_of_file = backup.read(60000000) # not exactly 60 MB....
response = connection.put() # submit part_of_file here to amazon