如何在Bash脚本中实现多线程以加速文本文件URL的curl请求?
在Bash中实现多线程加速curl循环的方法
首先得说一下,你现在脚本里加的&其实有问题——因为HTTP_RESPONSE这些变量是全局的,多个后台进程会同时修改它们,最后日志里的内容大概率是混乱的,根本达不到正确的并行效果。下面给你几种靠谱的实现方式,既能提速又能保证数据正确:
方法1:用xargs实现并行(最简单易上手)
xargs本身就支持并行参数,只需要把单站点处理逻辑封装成函数,再用xargs批量调用就行,代码改动很小:
SECRET_KEY='zuhahaha' FILE_NAME="" case "$1" in "sma") FILE_NAME="sma.txt" ;; "smk") FILE_NAME="smk.txt" ;; "smp") FILE_NAME="smp.txt" ;; "sd") FILE_NAME="sd.txt" ;; *) echo "not in case !" && exit 1 ;; esac # 封装单个站点的处理逻辑,用局部变量避免冲突 process_site() { local line="$1" local HTTP_RESPONSE=$(curl -L -s -w "HTTPSTATUS:%{http_code}\\n" -H "X-Gitlab-Event: Push Hook" -H "X-Gitlab-Token: $SECRET_KEY" --insecure "$line" 2>&1) local HTTP_BODY=$(echo "$HTTP_RESPONSE" | sed -e 's/HTTPSTATUS\:.*//g') local HTTP_STATUS=$(echo "$HTTP_RESPONSE" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') # 加锁写日志,防止多个进程同时写导致内容错乱 flock -x output.log -c " printf '%s\n' \ \"Header Code : $HTTP_STATUS\" \ \"Executed at : $(date)\" \ \"Response Body : $HTTP_BODY\" \ \"=====================================================================================================\" \ \"\" >> output.log " } # 导出函数和环境变量,让xargs的子进程能访问到 export -f process_site export SECRET_KEY # -n 1:每次给函数传1行内容;-P 10:同时跑10个进程(可以根据服务器性能调整) cat "$FILE_NAME" | xargs -n 1 -P 10 bash -c 'process_site "$@"' _
方法2:纯Bash后台进程+并发控制
如果不想用xargs,想自己手动控制并发数,可以用计数器配合wait -n来实现:
SECRET_KEY='zuhahaha' FILE_NAME="" case "$1" in "sma") FILE_NAME="sma.txt" ;; "smk") FILE_NAME="smk.txt" ;; "smp") FILE_NAME="smp.txt" ;; "sd") FILE_NAME="sd.txt" ;; *) echo "not in case !" && exit 1 ;; esac MAX_PROCS=10 # 最大并发数,按需调整 proc_count=0 save_log() { # 同样加锁保证日志写入的原子性 flock -x output.log -c " printf '%s\n' \ \"Header Code : $1\" \ \"Executed at : $(date)\" \ \"Response Body : $2\" \ \"=====================================================================================================\" \ \"\" >> output.log " } process_site() { local line="$1" local HTTP_RESPONSE=$(curl -L -s -w "HTTPSTATUS:%{http_code}\\n" -H "X-Gitlab-Event: Push Hook" -H "X-Gitlab-Token: $SECRET_KEY" --insecure "$line" 2>&1) local HTTP_BODY=$(echo "$HTTP_RESPONSE" | sed -e 's/HTTPSTATUS\:.*//g') local HTTP_STATUS=$(echo "$HTTP_RESPONSE" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') save_log "$HTTP_STATUS" "$HTTP_BODY" } while IFS= read -r line; do # 启动后台进程处理当前站点 process_site "$line" & proc_count=$((proc_count + 1)) # 如果达到最大并发数,等待任意一个后台进程结束再继续 if [ "$proc_count" -ge "$MAX_PROCS" ]; then wait -n # 这个命令需要Bash 4.3及以上版本支持 proc_count=$((proc_count - 1)) fi done < "$FILE_NAME" # 等待所有剩余的后台进程完成 wait
方法3:用parallel工具(功能更强大)
如果你的系统能安装parallel(比如Debian/Ubuntu用apt install parallel),它比xargs更灵活,支持负载均衡、失败重试、进度显示等功能:
SECRET_KEY='zuhahaha' FILE_NAME="" case "$1" in "sma") FILE_NAME="sma.txt" ;; "smk") FILE_NAME="smk.txt" ;; "smp") FILE_NAME="smp.txt" ;; "sd") FILE_NAME="sd.txt" ;; *) echo "not in case !" && exit 1 ;; esac process_site() { local line="$1" local HTTP_RESPONSE=$(curl -L -s -w "HTTPSTATUS:%{http_code}\\n" -H "X-Gitlab-Event: Push Hook" -H "X-Gitlab-Token: $SECRET_KEY" --insecure "$line" 2>&1) local HTTP_BODY=$(echo "$HTTP_RESPONSE" | sed -e 's/HTTPSTATUS\:.*//g') local HTTP_STATUS=$(echo "$HTTP_RESPONSE" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') flock -x output.log -c " printf '%s\n' \ \"Header Code : $HTTP_STATUS\" \ \"Executed at : $(date)\" \ \"Response Body : $HTTP_BODY\" \ \"=====================================================================================================\" \ \"\" >> output.log " } export -f process_site export SECRET_KEY # -j 10:指定并发数;:::: 表示从文件读取输入 parallel -j 10 process_site :::: "$FILE_NAME"
关键注意点
- 一定要用局部变量(
local关键字),避免多个后台进程互相覆盖变量值; - 写日志的时候必须加锁(比如
flock),否则多个进程同时写入会导致日志内容错乱; - 并发数不要设置得太高,不然可能会触发目标站点的限流,或者把自己的服务器资源占满。
内容的提问来源于stack exchange,提问作者0x00b0
相关产品推荐
相关产品推荐

