如何通过AWS EMR并行化curl拉取大文件至AWS S3
方案可行性与简单实现方法
这个并行分片拉取+S3合并的方案完全可行,是大文件跨网络传输加速的典型思路,下面给你具体的落地步骤:
1. 先确认源文件支持Range请求
首先要验证目标下载链接支持分片拉取,用curl发送HEAD请求:
curl -I "https://download-link-address/"
检查响应头里是否包含Accept-Ranges: bytes,如果有,说明可以用Range header分片拉取;如果没有,这个方案就无法使用,得换其他方式。
同时顺便获取文件总大小,从Content-Length字段拿到总字节数:
FILE_SIZE=$(curl -I "https://download-link-address/" | grep -i Content-Length | awk '{print $2}' | tr -d '\r')
2. 规划分片规则
设定每个分片的大小,比如选1GB(1073741824字节)——这个大小符合S3分块上传的要求(单分块5MB-5GB),能平衡并行效率和开销。计算总分片数:
PART_SIZE=$((1024*1024*1024)) TOTAL_PARTS=$(( (FILE_SIZE + PART_SIZE - 1) / PART_SIZE ))
3. EMR集群并行拉取分片
不用搭建复杂的Spark/Hadoop作业,直接用EMR的集群节点跑并行Shell任务即可:
方式一:用xargs并行执行(简单快捷)
在EMR主节点上生成所有分片拉取的命令,保存到tasks.txt:
> tasks.txt for ((i=0; i<TOTAL_PARTS; i++)); do START=$((i*PART_SIZE)) if [ $i -eq $((TOTAL_PARTS-1)) ]; then END=$((FILE_SIZE-1)) else END=$(((i+1)*PART_SIZE - 1)) fi echo "curl -H \"Range: bytes=$START-$END\" \"https://download-link-address/\" | aws s3 cp - s3://aws-bucket/temp-parts/part-$i" >> tasks.txt done
然后用xargs指定并行数执行(比如10个并行任务,根据集群带宽和源服务器承受能力调整):
cat tasks.txt | xargs -P 10 -I {} sh -c {}
如果需要弹性扩容,可以给EMR集群添加Task节点,然后提高xargs的-P值,加快拉取速度。
方式二:用EMR Steps批量执行(更可控)
如果需要更规范的任务管理,可以把分片命令做成EMR Step,每个分片对应一个Step,或者用Step的Shell脚本批量生成任务。不过对于简单场景,xargs足够用了。
4. 合并S3分片到目标文件
方法一:用S3分块上传API(推荐)
这种方法直接利用S3的分块上传机制,不需要额外的合并计算,效率最高:
- 初始化分块上传:
UPLOAD_ID=$(aws s3api create-multipart-upload --bucket aws-bucket --key data-file --query 'UploadId' --output text)
- 循环上传每个分片作为分块,记录每个分块的ETag:
> parts.json for ((i=0; i<TOTAL_PARTS; i++)); do ETag=$(aws s3api upload-part --bucket aws-bucket --key data-file --part-number $((i+1)) --body s3://aws-bucket/temp-parts/part-$i --upload-id $UPLOAD_ID --query 'ETag' --output text) echo "{\"PartNumber\": $((i+1)), \"ETag\": $ETag}" >> parts.json done # 整理成S3要求的JSON格式 sed -i '1s/^/[\n/; $s/$/\n]/' parts.json sed -i '$!s/$/,/' parts.json
- 完成分块上传:
aws s3api complete-multipart-upload --bucket aws-bucket --key data-file --upload-id $UPLOAD_ID --multipart-upload file://parts.json
- 清理临时分片:
aws s3 rm s3://aws-bucket/temp-parts/ --recursive
方法二:直接合并分片(适合分片数少的场景)
如果分片不多,可以直接在EC2/EMR节点上合并后上传:
# 按顺序合并所有分片并上传 cat $(for i in $(seq 0 $((TOTAL_PARTS-1))); do echo <(aws s3 cp s3://aws-bucket/temp-parts/part-$i -); done) | aws s3 cp - s3://aws-bucket/data-file # 清理临时分片 aws s3 rm s3://aws-bucket/temp-parts/ --recursive
额外优化点
- 断点续传:在生成任务时,先检查S3中对应的分片是否存在,存在就跳过,避免重复拉取:
if ! aws s3 ls s3://aws-bucket/temp-parts/part-$i > /dev/null 2>&1; then echo "curl -H \"Range: bytes=$START-$END\" \"https://download-link-address/\" | aws s3 cp - s3://aws-bucket/temp-parts/part-$i" >> tasks.txt fi - 成本控制:EMR集群可以用按需实例+Spot实例的组合,任务完成后及时终止集群,或者只保留主节点,Task节点按需添加/释放。
- 并行数调整:根据源服务器的带宽限制和EMR集群的网络能力调整
xargs -P的数值,避免触发源服务器的限流,或者集群带宽饱和。
内容的提问来源于stack exchange,提问作者Doug MacArthur
相关产品推荐
相关产品推荐

