从S3存储桶下载300万个对象的最快方法

32

我尝试使用Python + boto + multiprocessing、S3cmd和J3tset,但是在所有这些工具上都遇到了困难。

有什么建议吗?也许你一直在使用的现成脚本或其他我不知道的方法?

编辑:

eventlet+boto是一个值得考虑的解决方案,如下所述。在这里找到了一篇很好的eventlet参考文章 http://web.archive.org/web/20110520140439/http://teddziuba.com/2010/02/eventlet-asynchronous-io-for-g.html

我在下面添加了我正在使用的python脚本。


亚马逊进出口 http://aws.amazon.com/importexport/ :) - J-16 SDiZ
个别对象有多大? - Amber
@J.16.SDiZ 我等不了那么久 :( - Jagtesh Chadha
@Amber 大小在10kb-200kb之间,但大部分都在100kb以下。 - Jagtesh Chadha
我主要想知道是否值得获取一个EC2实例,将S3上的文件压缩成更大的包以节省请求次数。 - Amber
2个回答

36

好的,我根据@Matt Billenstien的提示想出了一个解决方案。它使用eventlet图书馆。这里最重要的第一步是(标准IO库的猴子补丁)。

在后台使用nohup运行此脚本,然后您就可以开始了。

from eventlet import *
patcher.monkey_patch(all=True)

import os, sys, time
from boto.s3.connection import S3Connection
from boto.s3.bucket import Bucket

import logging

logging.basicConfig(filename="s3_download.log", level=logging.INFO)


def download_file(key_name):
    # Its imp to download the key from a new connection
    conn = S3Connection("KEY", "SECRET")
    bucket = Bucket(connection=conn, name="BUCKET")
    key = bucket.get_key(key_name)

    try:
        res = key.get_contents_to_filename(key.name)
    except:
        logging.info(key.name+":"+"FAILED")

if __name__ == "__main__":
    conn = S3Connection("KEY", "SECRET")
    bucket = Bucket(connection=conn, name="BUCKET")

    logging.info("Fetching bucket list")
    bucket_list = bucket.list(prefix="PREFIX")

    logging.info("Creating a pool")
    pool = GreenPool(size=20)

    logging.info("Saving files in bucket...")
    for key in bucket.list():
        pool.spawn_n(download_file, key.key)
    pool.waitall()

2
注意,如果我在每个greenlet中不创建连接,就会出现问题。你能够使用这个下载所有的对象吗? - Matt Billenstein
2
不,我也遇到了问题。在下载了4000个对象后,它停止工作了。我没有时间调试它,所以最终我使用了一个shell脚本中的s3cmd get来获取每个文件。我将S3上的文件名列表分成几个集合,并同时运行7-8个集合的脚本(因此在任何时候我都有7-8个s3cmd get请求)。使用boto的bucket.list()方法获取文件列表,然后使用split shell命令创建相同大小的集合。这可能比eventlet方法消耗更多的CPU,但它简单且能完成任务。 - Jagtesh Chadha
我已经编辑了代码,为每个文件下载创建了一个新的连接(这与绿色线程兼容)。 - Jagtesh Chadha
3
在结尾处添加pool.waitall(),否则代码将不起作用并在任何下载完成之前退出。 - Jan Vlcinsky
错误已修复 - 未使用bucket_list。由于被要求修改超过6个字符,还添加了有关缺少子目录的提示。 - Juraj

5

使用eventlet实现I/O并行,编写一个简单的函数使用urllib下载一个对象,然后使用GreenPile将其映射到输入url列表中--一个包含50到100个greenlets的堆栈应该足够...


1
谢谢你的提示。但这不是类似于使用multiprocessing.Pool吗? - Jagtesh Chadha

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接