本文目录导读:

Shell脚本实现重试退避策略有多种方式,以下是几种常见实现:
指数退避(Exponential Backoff)
#!/bin/bash
function retry_command() {
local max_attempts=${1:-5}
local initial_delay=${2:-1}
local backoff_factor=${3:-2}
local max_delay=${4:-60}
local attempt=1
local delay=$initial_delay
while [ $attempt -le $max_attempts ]; do
echo "尝试 $attempt/$max_attempts..."
if "$@${@:5}" 2>/dev/null; then
echo "成功执行"
return 0
fi
echo "失败,等待 ${delay}秒后重试..."
sleep $delay
# 计算下一次延迟
delay=$((delay * backoff_factor))
if [ $delay -gt $max_delay ]; then
delay=$max_delay
fi
((attempt++))
done
echo "所有重试都失败"
return 1
}
# 使用示例
retry_command 5 1 2 30 "your_command_here"
带随机抖动的指数退避
#!/bin/bash
function retry_with_jitter() {
local max_attempts=${1:-5}
local base_delay=${2:-2}
local backoff_factor=${3:-2}
local max_delay=${4:-120}
local attempt=1
local delay=$base_delay
while [ $attempt -le $max_attempts ]; do
echo "尝试 $attempt/$max_attempts..."
# 执行命令
if eval "${@:5}"; then
echo "成功执行"
return 0
fi
echo "失败,等待 ${delay}秒后重试..."
# 添加随机抖动
local jitter=$((RANDOM % 5 + 1))
sleep $((delay + jitter))
# 计算下一次延迟
delay=$((delay * backoff_factor))
if [ $delay -gt $max_delay ]; then
delay=$max_delay
fi
((attempt++))
done
echo "所有重试都失败"
return 1
}
固定间隔重试
#!/bin/bash
function retry_fixed_interval() {
local max_attempts=${1:-3}
local interval=${2:-5}
local attempt=1
while [ $attempt -le $max_attempts ]; do
echo "尝试 $attempt/$max_attempts..."
if "$@${@:3}"; then
echo "成功"
return 0
fi
if [ $attempt -lt $max_attempts ]; then
echo "失败,${interval}秒后重试..."
sleep $interval
fi
((attempt++))
done
echo "所有重试失败"
return 1
}
高级版本:支持错误处理和日志
#!/bin/bash
function advanced_retry() {
# 配置参数
local max_attempts=5
local initial_delay=1
local max_delay=60
local backoff_factor=2
local retryable_exit_codes=(1 2 3) # 可重试的错误码
local attempt=1
local delay=$initial_delay
local logfile="/var/log/retry.log"
while [ $attempt -le $max_attempts ]; do
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 尝试 $attempt/$max_attempts" | tee -a $logfile
# 执行命令并捕获输出和退出码
output=$("$@" 2>&1)
exit_code=$?
if [ $exit_code -eq 0 ]; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 成功执行" | tee -a $logfile
return 0
fi
# 检查退出码是否可重试
local retryable=false
for code in "${retryable_exit_codes[@]}"; do
if [ $exit_code -eq $code ]; then
retryable=true
break
fi
done
if [ "$retryable" = false ] && [ $attempt -lt $max_attempts ]; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 不可重试的错误码: $exit_code" | tee -a $logfile
return $exit_code
fi
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 失败 (退出码: $exit_code)" | tee -a $logfile
if [ $attempt -lt $max_attempts ]; then
echo "等待 ${delay}秒后重试..." | tee -a $logfile
sleep $delay
# 指数退避
delay=$((delay * backoff_factor))
[ $delay -gt $max_delay ] && delay=$max_delay
fi
((attempt++))
done
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 所有重试都失败" | tee -a $logfile
return 1
}
使用 curl 的重试示例
#!/bin/bash
# curl 内置的重试功能
function curl_with_retry() {
local url=$1
local max_retries=${2:-3}
curl \
--retry $max_retries \
--retry-delay 5 \
--retry-max-time 60 \
--retry-connrefused \
-s \
-o /dev/null \
-w "%{http_code}" \
"$url"
}
# 使用示例
curl_with_retry "https://api.example.com" 5
实际使用示例
#!/bin/bash
source ./retry_functions.sh
# 示例1: 重试文件下载
retry_with_jitter 5 2 2 30 "wget -q https://example.com/largefile.zip"
# 示例2: 重试数据库连接
retry_command 3 5 1 30 "mysql -h host -u user -ppassword -e 'SELECT 1'"
# 示例3: 重试 API 调用
advanced_retry "curl -X POST https://api.example.com/data"
# 示例4: 重试直到成功或超时
TIMEOUT=300
START_TIME=$(date +%s)
while true; do
if your_command; then
break
fi
CURRENT_TIME=$(date +%s)
ELAPSED=$((CURRENT_TIME - START_TIME))
if [ $ELAPSED -ge $TIMEOUT ]; then
echo "超时"
exit 1
fi
sleep 5
done
推荐的退避策略
- 指数退避:适用于瞬时故障
- 指数退避+随机抖动:避免惊群效应
- 固定间隔:简单可预测,适用于非临界操作
- 线性递增:适用于需要逐渐增加间隔的场景
选择哪种策略取决于你的具体需求和失败原因。