在分布式系统架构中,服务接口的可用性和稳定性直接影响业务连续性。当面临以下场景时,需批量巡检GET和POST接口:
上线验证:新版本发布后批量验证核心接口
故障恢复:异常数据修复后的批量重试机制
日常巡检:定时检查关键业务接口健康状态
压力测试:模拟批量请求进行性能基线测试
传统人工操作存在效率低下(500+请求需2小时)、漏检率高(人工疲劳导致)、结果不可追溯等问题。
#!/bin/bash
# 多方法接口巡检脚本
# 用法:./multi_method_check.sh [并发数] [重试次数] [超时时间]
# 配置区 ==============================================================
declare -a API_CONFIG=(
# 格式说明:
# "接口描述"
# "HTTP方法"
# "请求URL/路径"
# "Content-Type"
# "参数模板(printf格式)"
# "参数组1"
# "参数组2"
# GET示例:用户查询
"用户查询接口"
"GET"
"http://api.example.com/users"
""
"id=%s&name=%s"
"123 alice"
"456 bob"
# POST示例:创建订单(JSON格式)
"订单创建接口"
"POST"
"http://api.example.com/orders"
"application/json"
'{"order_id":"%s","amount":%.2f}'
"ORD001 100.50"
"ORD002 200.00"
# POST示例:表单提交
"用户注册接口"
"POST"
"http://api.example.com/register"
"application/x-www-form-urlencoded"
"username=%s&age=%d"
"charlie 25"
"david 30"
)
# 全局设置
DEFAULT_CONCURRENCY=5 # 默认并发数
DEFAULT_RETRY=3 # 默认重试次数
DEFAULT_TIMEOUT=10 # 默认超时(秒)
# ===================================================================
# 初始化执行参数
CONCURRENCY=${1:-$DEFAULT_CONCURRENCY}
MAX_RETRY=${2:-$DEFAULT_RETRY}
TIMEOUT=${3:-$DEFAULT_TIMEOUT}
LOG_FILE="api_check_$(date +%Y%m%d%H%M%S).csv"
TMP_PIPE=$(mktemp -u)
mkfifo $TMP_PIPE
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
# 创建日志文件头
echo "timestamp,api_name,method,status,http_code,time_total,url,payload,response" > $LOG_FILE
# URL编码函数
urlencode() {
local string="${1}"
local length="${#string}"
local encoded=""
for ((i = 0; i < length; i++)); do
local c="${string:i:1}"
case $c in
[a-zA-Z0-9.~_-]) encoded+="$c" ;;
*) encoded+=$(printf '%%%02X' "'$c") ;;
esac
done
echo "$encoded"
}
# 核心请求函数
execute_request() {
local api_name=$1
local method=$2
local base_url=$3
local content_type=$4
local template=$5
local params=($6)
local retry=0
local success=0
local final_url=""
local payload=""
local curl_cmd=""
# 参数校验
local expected_args=$(grep -o '%[^%]*' <<< "$template" | wc -l)
if [ ${#params[@]} -ne $expected_args ]; then
echo -e "${RED}[配置错误]${NC} ${api_name} 参数数量不匹配(预期${expected_args},实际${#params[@]})"
return 1
fi
# 构建请求
case $method in
GET)
# 处理GET参数编码
encoded_params=()
for param in "${params[@]}"; do
encoded_params+=("$(urlencode "$param")")
done
# 格式化查询字符串
query_string=$(printf "$template" "${encoded_params[@]}")
final_url="${base_url}?${query_string}"
curl_cmd="curl -G -sS"
;;
POST|PUT|PATCH)
# 处理POST参数
if [[ "$content_type" == "application/json" ]]; then
payload=$(printf "$template" "${params[@]}")
else
# 处理表单编码
encoded_params=()
for param in "${params[@]}"; do
encoded_params+=("$(urlencode "$param")")
done
payload=$(printf "$template" "${encoded_params[@]}")
fi
final_url=$base_url
curl_cmd="curl -X $method -d '$payload'"
;;
DELETE)
final_url=$base_url
curl_cmd="curl -X DELETE"
;;
*)
echo -e "${RED}[错误]${NC} 不支持的HTTP方法: $method"
return 1
;;
esac
# 添加公共curl参数
curl_cmd+=" --max-time $TIMEOUT"
curl_cmd+=" -w '\nHTTP_CODE:%{http_code} TIME:%{time_total}'"
[[ -n "$content_type" ]] && curl_cmd+=" -H 'Content-Type: $content_type'"
curl_cmd+=" $final_url"
# 执行请求
while [ $retry -lt $MAX_RETRY ]; do
local start_time=$(date +%s%3N)
# 执行并捕获响应
local response=$(eval "$curl_cmd" 2>&1)
local exit_code=$?
local end_time=$(date +%s%3N)
# 解析响应
local http_code=$(echo "$response" | awk -F 'HTTP_CODE:' '{print $2}' | cut -d' ' -f1)
local time_total=$(echo "$response" | awk -F 'TIME:' '{print $2}')
local response_body=$(echo "$response" | sed '/HTTP_CODE:/d')
# 判断成功条件
if [ $exit_code -eq 0 ] && [ "$http_code" -eq 200 ]; then
success=1
break
else
((retry++))
sleep $((retry * 2)) # 指数退避
fi
done
# 记录日志
local timestamp=$(date +"%Y-%m-%d %T")
local status=$([ $success -eq 1 ] && echo "SUCCESS" || echo "FAILURE")
local log_entry="$timestamp,\"$api_name\",$method,$status,$http_code,${time_total}s,\"$final_url\",\"${payload//\"/'\"'}\",\"${response_body//\"/'\"'}\""
echo "$log_entry" >> $LOG_FILE
# 控制台输出
if [ $success -eq 1 ]; then
printf "${GREEN}%-7s${NC} | ${CYAN}%-20s${NC} | ${BLUE}%-4s${NC} | 状态:%-6s | 耗时:%-5s\n" \
"[$method]" "$api_name" "$http_code" "$status" "${time_total}s"
else
printf "${RED}%-7s${NC} | ${CYAN}%-20s${NC} | ${BLUE}%-4s${NC} | 尝试:%-2d | 错误:%-6s\n" \
"[$method]" "$api_name" "$http_code" $retry "${exit_code}"
fi
}
# 主执行逻辑
main() {
echo -e "${YELLOW}=== 开始接口巡检 ==="
echo -e "并发数: $CONCURRENCY | 最大重试: $MAX_RETRY | 超时: ${TIMEOUT}s"
echo -e "===================================${NC}"
# 解析配置
local i=0
while [ $i -lt ${#API_CONFIG[@]} ]; do
api_name=${API_CONFIG[$i]}
((i++))
method=${API_CONFIG[$i]}
((i++))
base_url=${API_CONFIG[$i]}
((i++))
content_type=${API_CONFIG[$i]}
((i++))
template=${API_CONFIG[$i]}
((i++))
# 收集参数组
params_list=()
while [ $i -lt ${#API_CONFIG[@]} ] && [[ ! ${API_CONFIG[$i]} =~ ^(GET|POST|PUT|PATCH|DELETE)$ ]]; do
params_list+=("${API_CONFIG[$i]}")
((i++))
done
# 生成任务
for params in "${params_list[@]}"; do
echo "$api_name $method $base_url $content_type $template $params" > $TMP_PIPE
done
done
# 并行执行
cat $TMP_PIPE | xargs -P $CONCURRENCY -n 1 -I {} bash -c 'execute_request {}'
# 清理临时文件
rm -f $TMP_PIPE
# 结果统计
local total=$(grep -c "SUCCESS" $LOG_FILE)
local success=$(grep -c "SUCCESS" $LOG_FILE)
local failure=$((total - success))
echo -e "\n${YELLOW}========== 最终结果 =========="
echo -e "总请求数: $total"
echo -e "${GREEN}成功: $success${NC}"
echo -e "${RED}失败: $failure${NC}"
echo -e "日志文件: $LOG_FILE${NC}"
}
# 执行主函数
main
修改配置区:在API_CONFIG数组中按以下格式配置接口:
"接口描述" # 接口名称(任意字符串)
"HTTP方法" # GET/POST/PUT/PATCH/DELETE
"请求URL" # 接口地址(包含协议和域名)
"Content-Type" # 请求内容类型(GET可留空)
"参数模板" # printf格式字符串,用于生成参数
"参数组1" # 空格分隔的参数值(按模板顺序)
"参数组2" # 更多参数组...
执行脚本:
chmod +x multi_method_check.sh
./multi_method_check.sh 10 3 15 # 并发数10 重试3次 超时15秒
配置示例说明:
#GET请求示例(带查询参数)
"用户查询接口"
"GET"
"http://api.example.com/users"
"" # 内容类型留空
"id=%s&name=%s" # 参数模板
"123 alice" # 参数组1:id=123&name=alice
"456 bob" # 参数组2:id=456&name=bob
#POST JSON示例
"订单创建接口"
"POST"
"http://api.example.com/orders"
"application/json"
'{"order_id":"%s","amount":%.2f}'
"ORD001 100.50" # 生成JSON:{"order_id":"ORD001","amount":100.50}
"ORD002 200.00"
#POST表单示例
"用户注册接口"
"POST"
"http://api.example.com/register"
"application/x-www-form-urlencoded"
"username=%s&age=%d"
"charlie 25" # 生成:username=charlie&age=25
"david 30"
注意事项:
参数模板中的占位符数量必须与参数组的参数数量严格一致
特殊字符会自动进行URL编码处理
JSON格式请使用单引号定义模板字符串
建议在测试环境验证配置后再用于生产环境
日志文件包含敏感信息,请注意保管