分享一个可以批量巡检GET和POST接口的Shell脚本

一、场景痛点与需求分析

在分布式系统架构中,服务接口的可用性和稳定性直接影响业务连续性。当面临以下场景时,需批量巡检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格式请使用单引号定义模板字符串
建议在测试环境验证配置后再用于生产环境
日志文件包含敏感信息,请注意保管

你可能感兴趣的:(linux,运维)