使用Python多进程下载S3文件时出现HeadObject 404错误如何解决?
报错原因
- boto3的S3 Resource/Client实例不支持跨进程序列化传递,也不是进程安全的。你在父进程初始化的
s3_resource通过multiprocessing.Process的args参数传递给子进程时,会经过pickle序列化、子进程反序列化的流程,这个过程会破坏实例底层持有的HTTP连接池、签名凭证缓存、请求上下文等内部状态。 - S3为了避免未授权用户判断资源是否存在,签名异常、请求路径解析错误等非资源不存在的场景也可能返回404状态码,这也是你明明确认资源存在、权限正常却收到404的直接原因。你遇到的单个文件下载成功只是巧合,是某一个子进程反序列化后的实例刚好还保留了可用的状态。
解决方案
方案1:子进程内部单独初始化S3 Resource(推荐多进程场景使用)
不要在父进程创建S3实例后传递给子进程,改为每个子进程执行下载逻辑前单独初始化自己的S3实例:
import multiprocessing as mp import boto3 def download_parallel(bucket, prefix): # 子进程内部单独初始化S3资源 s3_resource = boto3.resource('s3') # path = compute where to download using prefix s3_resource.Object(bucket, prefix).download_file(path) p_list = [] for prefix in prefixes: # 不再传递s3_resource参数 p = mp.Process(target=download_parallel, args=(bucket, prefix)) p.start() p_list.append(p) for p in p_list: p.join()
方案2:改用多线程实现并行下载(推荐IO密集型场景使用)
S3下载属于IO密集型任务,多线程的开销远低于多进程,且boto3的S3实例是线程安全的,可以直接在多个线程中复用同一个实例:
from concurrent.futures import ThreadPoolExecutor import boto3 s3_resource = boto3.resource('s3') # 可以全局初始化复用 def download_parallel(bucket, prefix): # path = compute where to download using prefix s3_resource.Object(bucket, prefix).download_file(path) # 按需调整线程数,通常设置为10-30即可满足S3下载的带宽需求 with ThreadPoolExecutor(max_workers=10) as executor: for prefix in prefixes: executor.submit(download_parallel, bucket, prefix)
内容的提问来源于stack exchange,提问作者Rishabh
相关产品推荐
相关产品推荐

