【问题标题】:Downloading multiple S3 objects in parallel in Python在 Python 中并行下载多个 S3 对象
【发布时间】:2018-06-13 23:36:05
【问题描述】:

有没有办法在 Python3 中使用 boto3 同时下载 S3 文件? 我知道aiobotocore 库,但我想知道是否有办法使用标准的boto3 库来做到这一点。

【问题讨论】:

  • 通过查看at the code,我会说它已经自行完成(在我粘贴的链接中查找max_concurrency
  • 不应该使用aiobotocore的常见原因有哪些?

标签: python python-3.x amazon-s3 boto3 botocore


【解决方案1】:

如果您想使用boto3 将大量较小的文件直接并行下载到磁盘,您可以使用multiprocessing 模块执行此操作。这里有一个小sn-p 可以做到这一点。你像这样运行它:./download.py bucket_name s3_key_0 s3_key_1 ... s3_key_n

#!/usr/bin/env python3
import multiprocessing
import boto3
import sys

# make a per process s3_client
s3_client = None
def initialize():
  global s3_client
  s3_client = boto3.client('s3')

# the work function of each process which will fetch something from s3
def download(job):
  bucket, key, filename = job
  s3_client.download_file(bucket, key, filename)

if __name__ == '__main__':
  # make the jobs, arguments to program are: bucket s3_key_0 s3_key_1 ... s3_key_n
  bucket = sys.argv[1]
  jobs = [(bucket, key, key.replace('/', '_')) for key in sys.argv[2:] ]

  # make a process pool to do the work
  pool = multiprocessing.Pool(multiprocessing.cpu_count(), initialize)
  pool.map(download, jobs)
  pool.close()
  pool.join()

其中一个重要的部分是我们为每个进程将重用的每个进程创建一个 s3 客户端实例。这很重要,原因有两个。首先,创建客户端很慢,所以我们希望尽可能少地这样做。其次,客户端不应跨进程共享,因为对 download_file 的调用可能会改变客户端的内部状态。

【讨论】:

  • 尝试共享内部 s3 客户端状态的问题是为什么您需要使用多处理而不是多线程?我有类似的需要下载许多较小的文件,并且由于多处理与多线程池的启动时间较长而想知道是否成功
  • 线程池的多线程应该可以工作,只要您为池中的每个线程创建一个客户端。与使用线程本地存储的线程相比,多处理似乎使用 global 技巧来说明这一点的代码更少
  • 我尝试了所有线程的单个客户端和每个线程的客户端 - 两者都运行良好。 ...看起来 boto 团队存在重大(轻描淡写!)不确定性和缺乏明确性。
  • @Richard 他们可能故意混淆这些信息,以便他们更容易改变关于什么是好主意和不是好主意的核心假设。就像最近的版本可能成为线程安全的但旧版本不是。正如你所说,目前它是一个谜!
  • 不确定。绝对知道有一个github线程,他们被很多人反复询问了几个月,并且根本没有任何声明。不幸的是,我不认为他们有很大的责任感
【解决方案2】:

下面的 sn-p 将允许您使用多处理从 s3 下载多个对象

import boto3
import multiprocessing as mp
import os

s3 = boto3.resource('s3')
my_bucket = s3.Bucket('My_bucket')
        
def s3download(object_key_file):
    my_bucket.download_file(object_key_file[0], object_key_file[1])
    print('downloaded file with object name... {}'.format(object_key_file[0]))
    print('downloaded file with file name... {}'.format(object_key_file[1]))
        
def parallel_s3_download():
    object_key_file=[]
    for s3_object in my_bucket.objects.filter(Prefix="directory_name/"):
        # Need to split s3_object.key into path and file name, else it will give error file not found.
        path, filename = os.path.split(s3_object.key)
        object_key_file.append((s3_object.key,filename))
    object_key_file.pop(0)
    pool = mp.Pool(min(mp.cpu_count(), len(object_key_file)))  # number of workers
    pool.map(s3download, object_key_file, chunksize=1)
    pool.close()
if __name__ == "__main__":
    parallel_s3_download()
    print('downloading zip file')

【讨论】:

  • object_key_file.pop(0) 是干什么用的?另外,我收到此错误:TypeError: s3download() missing 1 required positional argument: 'object_key_file'
猜你喜欢
  • 2015-07-18
  • 2016-02-21
  • 1970-01-01
  • 2015-12-24
  • 2012-06-17
  • 2010-10-07
  • 1970-01-01
  • 2011-09-25
  • 2023-03-19
相关资源
最近更新 更多