压力测试

2025-12-16  本文已影响0人  Joening

简单的cpu压力测试脚本

#!/usr/bin/env python3
"""
最简单的CPU压力测试脚本
单文件,无需参数,直接运行即可让所有CPU核心满载
"""

import os
import multiprocessing
import math

def cpu_burn():
    """让单个CPU核心满载"""
    print(f"进程 {os.getpid()} 正在压测CPU...")
    while True:
        # 执行大量数学计算
        for i in range(1000000):
            math.sqrt(i) * math.sin(i)

if __name__ == "__main__":
    # 获取CPU核心数
    cpu_count = multiprocessing.cpu_count()
    print(f"检测到 {cpu_count} 个CPU核心")
    print(f"启动 {cpu_count} 个进程进行压力测试")
    print("按 Ctrl+C 停止测试\n")

    # 创建进程池
    processes = []
    for i in range(cpu_count):
        p = multiprocessing.Process(target=cpu_burn)
        p.start()
        processes.append(p)

    try:
        # 保持主进程运行
        for p in processes:
            p.join()
    except KeyboardInterrupt:
        print("\n正在停止所有进程...")
        for p in processes:
            p.terminate()
        print("压力测试已停止")

计算ns下面cpu,memory实际使用总和

#!/usr/bin/env bash
set -euo pipefail

####################  usage  ####################
usage() {
  cat <<EOF
Usage: $(basename "$0") [OPTIONS]

Options:
  -n <namespace>   目标命名空间(与 --all 互斥)
  --all            获取所有命名空间的资源使用率
  -h, --help       显示本帮助
EOF
  exit "$1"
}

####################  参数解析  ####################
Namespace=""
AllNamespaces=false

while [[ $# -gt 0 ]]; do
  case "$1" in
    -n)
      if [[ -n "$Namespace" ]]; then
        echo "Error: 不能同时指定 -n 和 --all" >&2
        usage 1
      fi
      Namespace="$2"
      shift 2
      ;;
    --all)
      if [[ -n "$Namespace" ]]; then
        echo "Error: 不能同时指定 -n 和 --all" >&2
        usage 1
      fi
      AllNamespaces=true
      shift
      ;;
    -h|--help)
      usage 0
      ;;
    *)
      echo "Error: 未知参数: $1" >&2
      usage 1
      ;;
  esac
done

if [[ -z "$Namespace" && "$AllNamespaces" == false ]]; then
  echo "Error: 必须指定 -n <namespace> 或 --all" >&2
  usage 1
fi

####################  函数定义  ####################
# 获取单个命名空间的资源使用率
get_namespace_stats() {
  local ns="$1"
  local raw

  echo "正在获取命名空间 '$ns' 的资源使用率..."

  raw=$(kubectl top pod -n "$ns" --no-headers --containers 2>&1) || {
    echo "Error: 无法获取命名空间 '$ns' 的指标数据" >&2
    echo "$raw" >&2
    return 1
  }

  if [[ -z "$raw" || "$raw" == *"No resources found"* ]]; then
    echo "Warning: 命名空间 '$ns' 中没有找到资源或无法获取指标。"
    return 0
  fi

  # 计算CPU和内存总量
  local cpu_m=$(echo "$raw" | awk '{sum += $3} END {print sum}')
  local mem_mi=$(echo "$raw" | awk '{sum += $4} END {print sum}')

  # 单位转换
  local cpu_cores=$(echo "scale=3; $cpu_m/1000" | bc 2>/dev/null || echo "0")
  local mem_gib=$(echo "scale=2; $mem_mi/1024" | bc 2>/dev/null || echo "0")

  # 输出结果
  printf "\n=== 命名空间: %s ===\n" "$ns"
  printf "总 CPU 使用: %.3f core\n" "$cpu_cores"
  printf "总 内存 使用: %.2f GiB\n" "$mem_gib"
  printf "Pod 数量: %d\n" "$(echo "$raw" | wc -l)"
}

# 获取所有命名空间的资源使用率
get_all_namespaces_stats() {
  local namespaces
  local total_cpu_cores=0
  local total_mem_gib=0
  local total_pods=0

  echo "正在获取所有命名空间的资源使用率..."

  # 获取所有命名空间列表
  namespaces=$(kubectl get namespaces -o jsonpath='{.items[*].metadata.name}' 2>/dev/null) || {
    echo "Error: 无法获取命名空间列表" >&2
    return 1
  }

  if [[ -z "$namespaces" ]]; then
    echo "Warning: 没有找到任何命名空间"
    return 0
  fi

  echo "=============================================="

  # 遍历每个命名空间
  for ns in $namespaces; do
    local raw

    raw=$(kubectl top pod -n "$ns" --no-headers --containers 2>/dev/null) || continue

    if [[ -z "$raw" || "$raw" == *"No resources found"* ]]; then
      continue
    fi

    # 计算当前命名空间的资源
    local cpu_m=$(echo "$raw" | awk '{sum += $3} END {print sum}')
    local mem_mi=$(echo "$raw" | awk '{sum += $4} END {print sum}')
    local pod_count=$(echo "$raw" | wc -l)

    # 单位转换
    local cpu_cores=$(echo "scale=3; $cpu_m/1000" | bc 2>/dev/null || echo "0")
    local mem_gib=$(echo "scale=2; $mem_mi/1024" | bc 2>/dev/null || echo "0")

    # 累加到总计
    total_cpu_cores=$(echo "$total_cpu_cores + $cpu_cores" | bc 2>/dev/null || echo "$total_cpu_cores")
    total_mem_gib=$(echo "$total_mem_gib + $mem_gib" | bc 2>/dev/null || echo "$total_mem_gib")
    total_pods=$((total_pods + pod_count))

    # 输出当前命名空间结果
    printf "命名空间: %-20s CPU: %8.3f core   内存: %8.2f GiB   Pod: %4d\n" \
      "$ns" "$cpu_cores" "$mem_gib" "$pod_count"
  done

  echo "=============================================="
  printf "\n总计:\n"
  printf "总 CPU 使用: %.3f core\n" "$total_cpu_cores"
  printf "总 内存 使用: %.2f GiB\n" "$total_mem_gib"
  printf "总 Pod 数量: %d\n" "$total_pods"
}

####################  主逻辑  ####################
# 检查 kubectl 是否可用
if ! command -v kubectl >/dev/null 2>&1; then
  echo "Error: kubectl 命令未找到,请确保已安装并配置 kubectl" >&2
  exit 3
fi

# 检查是否能连接到 Kubernetes 集群
if ! kubectl cluster-info >/dev/null 2>&1; then
  echo "Error: 无法连接到 Kubernetes 集群,请检查集群配置" >&2
  exit 4
fi

# 执行相应的功能
if [[ "$AllNamespaces" == true ]]; then
  get_all_namespaces_stats
else
  get_namespace_stats "$Namespace"
fi

exit 
#!/bin/bash

# http_qps_detailed.sh - HTTP 请求 QPS 测试工具(带详细执行情况)
# 版本: 2.0

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
WHITE='\033[1;37m'
NC='\033[0m' # 无颜色

# 默认参数
DEFAULT_URL="http://localhost:8080"
DEFAULT_REQUESTS=100
DEFAULT_CONCURRENCY=1
DEFAULT_TIMEOUT=5
DEFAULT_METHOD="GET"
DEFAULT_REPORT_INTERVAL=10 # 报告间隔(百分比)

# 全局变量
TOTAL_START_TIME=0
TOTAL_SUCCESS=0
TOTAL_FAILED=0
TOTAL_TIMEOUT=0
CURRENT_BATCH=0
STATUS_CODES=()
RESPONSE_TIMES=()
REQUEST_LOG=()

# 显示帮助信息
show_help() {
    echo -e "${CYAN}HTTP QPS 测试工具(带详细执行情况)${NC}"
    echo "========================================"
    echo "用法: $0 [选项]"
    echo ""
    echo "选项:"
    echo "  -u, --url URL          目标URL (默认: $DEFAULT_URL)"
    echo "  -n, --requests N       总请求次数 (默认: $DEFAULT_REQUESTS)"
    echo "  -c, --concurrency N    并发数 (默认: $DEFAULT_CONCURRENCY)"
    echo "  -t, --timeout N        超时时间(秒) (默认: $DEFAULT_TIMEOUT)"
    echo "  -m, --method METHOD    HTTP方法 (默认: $DEFAULT_METHOD)"
    echo "  -i, --interval N       报告间隔百分比 (默认: $DEFAULT_REPORT_INTERVAL)"
    echo "  -H, --header HEADER    请求头 (可多次使用)"
    echo "  -d, --data DATA        POST数据"
    echo "  -w, --warmup N         预热请求数 (默认: 0)"
    echo "  -o, --output FILE      输出结果到文件"
    echo "  -v, --verbose          显示每个请求的详细信息"
    echo "  -q, --quiet            静默模式(只显示最终结果)"
    echo "  -h, --help             显示此帮助信息"
    echo ""
    echo "示例:"
    echo "  $0 -u http://api.example.com -n 1000 -c 10 -i 5"
    echo "  $0 --url http://localhost:3000 --requests 500 --concurrency 5 --interval 20"
    echo "  $0 -u http://example.com -n 200 -c 10 -v"
    echo "========================================"
}

# 打印函数
print_info() {
    if [ "$QUIET" != "true" ]; then
        echo -e "${BLUE}[INFO]${NC} $1"
    fi
}

print_success() {
    if [ "$QUIET" != "true" ]; then
        echo -e "${GREEN}[SUCCESS]${NC} $1"
    fi
}

print_warning() {
    if [ "$QUIET" != "true" ]; then
        echo -e "${YELLOW}[WARNING]${NC} $1"
    fi
}

print_error() {
    echo -e "${RED}[ERROR]${NC} $1" >&2
}

print_verbose() {
    if [ "$VERBOSE" = "true" ] && [ "$QUIET" != "true" ]; then
        echo -e "${PURPLE}[VERBOSE]${NC} $1"
    fi
}

print_debug() {
    if [ "$DEBUG" = "true" ] && [ "$QUIET" != "true" ]; then
        echo -e "${CYAN}[DEBUG]${NC} $1"
    fi
}

# 检查依赖
check_dependencies() {
    if ! command -v curl &> /dev/null; then
        print_error "需要 curl 命令,请先安装: sudo apt-get install curl"
        exit 1
    fi
}

# 解析参数
parse_args() {
    HEADERS_STRING=""

    while [[ $# -gt 0 ]]; do
        case $1 in
            -u|--url)
                URL="$2"
                shift 2
                ;;
            -n|--requests)
                REQUESTS="$2"
                shift 2
                ;;
            -c|--concurrency)
                CONCURRENCY="$2"
                shift 2
                ;;
            -t|--timeout)
                TIMEOUT="$2"
                shift 2
                ;;
            -m|--method)
                METHOD="$2"
                shift 2
                ;;
            -i|--interval)
                REPORT_INTERVAL="$2"
                shift 2
                ;;
            -H|--header)
                HEADERS_STRING="$HEADERS_STRING -H \"$2\""
                shift 2
                ;;
            -d|--data)
                POST_DATA="$2"
                shift 2
                ;;
            -w|--warmup)
                WARMUP="$2"
                shift 2
                ;;
            -o|--output)
                OUTPUT_FILE="$2"
                shift 2
                ;;
            -v|--verbose)
                VERBOSE="true"
                shift
                ;;
            -q|--quiet)
                QUIET="true"
                VERBOSE="false"
                shift
                ;;
            --debug)
                DEBUG="true"
                shift
                ;;
            -h|--help)
                show_help
                exit 0
                ;;
            *)
                print_error "未知参数: $1"
                show_help
                exit 1
                ;;
        esac
    done

    # 设置默认值
    URL=${URL:-$DEFAULT_URL}
    REQUESTS=${REQUESTS:-$DEFAULT_REQUESTS}
    CONCURRENCY=${CONCURRENCY:-$DEFAULT_CONCURRENCY}
    TIMEOUT=${TIMEOUT:-$DEFAULT_TIMEOUT}
    METHOD=${METHOD:-$DEFAULT_METHOD}
    REPORT_INTERVAL=${REPORT_INTERVAL:-$DEFAULT_REPORT_INTERVAL}
    WARMUP=${WARMUP:-0}
    VERBOSE=${VERBOSE:-false}
    QUIET=${QUIET:-false}
    DEBUG=${DEBUG:-false}

    # 验证参数
    if [[ ! "$REQUESTS" =~ ^[0-9]+$ ]] || [ "$REQUESTS" -le 0 ]; then
        print_error "请求次数必须为正整数"
        exit 1
    fi

    if [[ ! "$CONCURRENCY" =~ ^[0-9]+$ ]] || [ "$CONCURRENCY" -le 0 ]; then
        print_error "并发数必须为正整数"
        exit 1
    fi

    if [ "$CONCURRENCY" -gt "$REQUESTS" ]; then
        CONCURRENCY="$REQUESTS"
        print_warning "并发数大于总请求数,已调整为: $CONCURRENCY"
    fi

    if [[ ! "$TIMEOUT" =~ ^[0-9]+$ ]] || [ "$TIMEOUT" -le 0 ]; then
        print_error "超时时间必须为正整数"
        exit 1
    fi

    if [[ ! "$REPORT_INTERVAL" =~ ^[0-9]+$ ]] || [ "$REPORT_INTERVAL" -le 0 ] || [ "$REPORT_INTERVAL" -gt 100 ]; then
        print_error "报告间隔必须为1-100之间的整数"
        exit 1
    fi

    if [[ ! "$WARMUP" =~ ^[0-9]+$ ]]; then
        print_error "预热请求数必须为非负整数"
        exit 1
    fi
}

# 显示测试配置
show_config() {
    echo -e "${CYAN}========================================${NC}"
    echo -e "${WHITE}        HTTP QPS 测试配置${NC}"
    echo -e "${CYAN}========================================${NC}"
    echo -e "${BLUE}目标URL:${NC}     $URL"
    echo -e "${BLUE}HTTP方法:${NC}    $METHOD"
    echo -e "${BLUE}总请求数:${NC}    $REQUESTS"
    echo -e "${BLUE}并发数:${NC}      $CONCURRENCY"
    echo -e "${BLUE}超时时间:${NC}    ${TIMEOUT}s"
    echo -e "${BLUE}报告间隔:${NC}    每${REPORT_INTERVAL}%报告一次"
    echo -e "${BLUE}预热请求:${NC}    $WARMUP"

    if [ -n "$POST_DATA" ]; then
        echo -e "${BLUE}POST数据:${NC}   ${POST_DATA:0:50}$([ ${#POST_DATA} -gt 50 ] && echo "...")"
    fi

    if [ -n "$HEADERS_STRING" ]; then
        echo -e "${BLUE}请求头:${NC}     已设置 $(echo "$HEADERS_STRING" | tr -cd '"' | wc -c) 个"
    fi

    echo -e "${CYAN}========================================${NC}"
    echo ""
}

# 发送单个请求
send_single_request() {
    local req_id="$1"
    local phase="${2:-test}"
    local start_time
    local end_time
    local http_code
    local response_time_ms
    local curl_output
    local curl_exit_code

    start_time=$(date +%s%N)

    # 构建curl命令
    local curl_cmd="curl -s"

    # 添加方法
    if [ "$METHOD" != "GET" ]; then
        curl_cmd="$curl_cmd -X $METHOD"
    fi

    # 添加超时
    curl_cmd="$curl_cmd --max-time $TIMEOUT"

    # 添加请求头
    if [ -n "$HEADERS_STRING" ]; then
        eval "curl_cmd=\"$curl_cmd $HEADERS_STRING\""
    fi

    # 添加POST数据
    if [ -n "$POST_DATA" ] && { [ "$METHOD" = "POST" ] || [ "$METHOD" = "PUT" ]; }; then
        curl_cmd="$curl_cmd -d '$POST_DATA'"
    fi

    # 添加URL和输出格式
    curl_cmd="$curl_cmd -w \"HTTP_CODE:%{http_code} TIME_TOTAL:%{time_total} SIZE_DOWNLOAD:%{size_download} SPEED_DOWNLOAD:%{speed_download}\" \"$URL\" 2>&1"

    # 执行curl命令
    print_verbose "请求 #$req_id: 发送请求到 $URL"

    curl_output=$(eval "$curl_cmd")
    curl_exit_code=$?

    end_time=$(date +%s%N)

    # 计算响应时间(毫秒)
    response_time_ms=$(( (end_time - start_time) / 1000000 ))

    # 解析curl输出
    http_code=$(echo "$curl_output" | grep -o "HTTP_CODE:[0-9]*" | cut -d: -f2)

    if [ -z "$http_code" ]; then
        if [ $curl_exit_code -eq 28 ]; then
            http_code="TIMEOUT"
            response_time_ms=$((TIMEOUT * 1000))
            print_verbose "请求 #$req_id: 超时 (${TIMEOUT}s)"
        else
            http_code="ERROR"
            print_verbose "请求 #$req_id: 连接错误 (退出码: $curl_exit_code)"
        fi
    else
        local time_total=$(echo "$curl_output" | grep -o "TIME_TOTAL:[0-9.]*" | cut -d: -f2)
        local size_download=$(echo "$curl_output" | grep -o "SIZE_DOWNLOAD:[0-9]*" | cut -d: -f2)
        local speed_download=$(echo "$curl_output" | grep -o "SPEED_DOWNLOAD:[0-9]*" | cut -d: -f2)

        print_verbose "请求 #$req_id: HTTP $http_code, 耗时: ${time_total}s, 大小: ${size_download}字节, 速度: ${speed_download}字节/秒"
    fi

    # 记录结果
    if [ "$phase" = "test" ]; then
        REQUEST_LOG+=("$req_id,$http_code,$response_time_ms,$(date +%H:%M:%S.%3N)")

        if [[ "$http_code" =~ ^2[0-9][0-9]$ ]] || [[ "$http_code" =~ ^3[0-9][0-9]$ ]]; then
            TOTAL_SUCCESS=$((TOTAL_SUCCESS + 1))
            STATUS_CODES+=("$http_code")
            RESPONSE_TIMES+=("$response_time_ms")
            return 0
        elif [ "$http_code" = "TIMEOUT" ]; then
            TOTAL_TIMEOUT=$((TOTAL_TIMEOUT + 1))
            TOTAL_FAILED=$((TOTAL_FAILED + 1))
            return 1
        else
            TOTAL_FAILED=$((TOTAL_FAILED + 1))
            return 1
        fi
    fi

    return 0
}

# 预热阶段
warmup_phase() {
    if [ "$WARMUP" -gt 0 ]; then
        print_info "开始预热阶段: $WARMUP 个请求..."

        for ((i=1; i<=WARMUP; i++)); do
            send_single_request "$i" "warmup" &

            # 控制并发
            if [ $((i % CONCURRENCY)) -eq 0 ] || [ $i -eq $WARMUP ]; then
                wait
            fi

            # 显示进度
            if [ $((i % 10)) -eq 0 ]; then
                echo -ne "\r预热进度: $i/$WARMUP"
            fi
        done

        echo ""
        print_info "预热完成"
        echo ""
    fi
}

# 显示进度条
show_progress_bar() {
    local current=$1
    local total=$2
    local width=30
    local percent=$((current * 100 / total))
    local completed=$((percent * width / 100))
    local remaining=$((width - completed))

    printf "\r${CYAN}进度:${NC} [%s%s] %3d%% (%d/%d)" \
        "$(printf '█%.0s' $(seq 1 $completed))" \
        "$(printf '░%.0s' $(seq 1 $remaining))" \
        "$percent" \
        "$current" \
        "$total"
}

# 显示实时统计
show_realtime_stats() {
    local current=$1
    local total=$2
    local batch_start_time=$3

    if [ "$QUIET" = "true" ]; then
        return
    fi

    local current_time=$(date +%s%N)
    local elapsed_ns=$((current_time - TOTAL_START_TIME))
    local elapsed_ms=$((elapsed_ns / 1000000))

    # 计算当前QPS
    local current_qps=0
    if [ $elapsed_ms -gt 0 ]; then
        current_qps=$((current * 1000 / elapsed_ms))
    fi

    # 计算批次QPS
    local batch_elapsed_ms=0
    local batch_qps=0
    if [ -n "$batch_start_time" ]; then
        batch_elapsed_ms=$((current_time - batch_start_time))
        if [ $batch_elapsed_ms -gt 0 ]; then
            local batch_size=$((current - CURRENT_BATCH))
            batch_qps=$((batch_size * 1000 / batch_elapsed_ms))
        fi
    fi

    # 清空行并显示统计
    printf "\033[2K\r"
    show_progress_bar "$current" "$total"

    echo -n " | "
    echo -n "成功: ${GREEN}$TOTAL_SUCCESS${NC} "
    echo -n "失败: ${RED}$TOTAL_FAILED${NC} "
    echo -n "超时: ${YELLOW}$TOTAL_TIMEOUT${NC} "
    echo -n "| QPS: ${WHITE}$current_qps${NC}"

    if [ $batch_qps -gt 0 ]; then
        echo -n " (批次: ${WHITE}$batch_qps${NC})"
    fi

    echo -n " | 耗时: ${BLUE}$((elapsed_ms / 1000)).$((elapsed_ms % 1000))s${NC}"
}

# 显示详细报告
show_detailed_report() {
    local current=$1
    local total=$2
    local force="${3:-false}"

    # 检查是否需要报告
    if [ $((current * 100 / total)) -lt $((LAST_REPORT_PERCENT + REPORT_INTERVAL)) ] && [ "$force" != "true" ]; then
        return
    fi

    LAST_REPORT_PERCENT=$((current * 100 / total))

    local current_time=$(date +%s%N)
    local elapsed_ns=$((current_time - TOTAL_START_TIME))
    local elapsed_ms=$((elapsed_ns / 1000000))
    local elapsed_sec=$((elapsed_ms / 1000))

    # 计算QPS
    local qps=0
    if [ $elapsed_sec -gt 0 ]; then
        qps=$((current / elapsed_sec))
    fi

    # 计算精确QPS
    local precise_qps=0
    if [ $elapsed_ms -gt 0 ]; then
        precise_qps=$((current * 1000 / elapsed_ms))
    fi

    echo ""
    echo -e "${CYAN}════════════════════════════════════════${NC}"
    echo -e "${WHITE}         进度报告 - ${LAST_REPORT_PERCENT}%${NC}"
    echo -e "${CYAN}════════════════════════════════════════${NC}"
    echo -e "时间:          $(date +%H:%M:%S)"
    echo -e "进度:          $current / $total (${LAST_REPORT_PERCENT}%)"
    echo -e "运行时间:      ${elapsed_sec}.$((elapsed_ms % 1000)) 秒"
    echo -e "当前QPS:       ${qps} 请求/秒 (精确: ${precise_qps}.$(( (current * 1000 * 10 / elapsed_ms) % 10 )) )"
    echo -e "成功请求:      ${GREEN}$TOTAL_SUCCESS${NC}"
    echo -e "失败请求:      ${RED}$TOTAL_FAILED${NC}"
    echo -e "超时请求:      ${YELLOW}$TOTAL_TIMEOUT${NC}"
    echo -e "成功率:        $((TOTAL_SUCCESS * 100 / current))%"

    # 显示最近5个请求的情况
    if [ ${#REQUEST_LOG[@]} -gt 0 ]; then
        echo -e "${CYAN}最近请求状态:${NC}"
        local start_idx=$(( ${#REQUEST_LOG[@]} - 5 ))
        if [ $start_idx -lt 0 ]; then
            start_idx=0
        fi

        for ((i=start_idx; i<${#REQUEST_LOG[@]}; i++)); do
            local log_entry="${REQUEST_LOG[$i]}"
            local req_id=$(echo "$log_entry" | cut -d, -f1)
            local http_code=$(echo "$log_entry" | cut -d, -f2)
            local response_time=$(echo "$log_entry" | cut -d, -f3)
            local timestamp=$(echo "$log_entry" | cut -d, -f4)

            local color="$GREEN"
            if [ "$http_code" = "TIMEOUT" ] || [ "$http_code" = "ERROR" ]; then
                color="$RED"
            elif [[ "$http_code" =~ ^4[0-9][0-9]$ ]] || [[ "$http_code" =~ ^5[0-9][0-9]$ ]]; then
                color="$YELLOW"
            fi

            echo -e "  #$req_id: ${color}HTTP $http_code${NC} - ${response_time}ms ($timestamp)"
        done
    fi

    echo -e "${CYAN}════════════════════════════════════════${NC}"
    echo ""
}

# 运行主测试
run_test() {
    print_info "开始 HTTP QPS 测试"
    echo ""

    show_config

    # 记录开始时间
    TOTAL_START_TIME=$(date +%s%N)
    LAST_REPORT_PERCENT=0

    # 预热
    warmup_phase

    # 开始主测试
    print_info "开始主测试..."
    echo ""

    # 改进的并发处理:分批启动请求,但批次更小以模拟持续并发
    print_info "启动并发请求测试..."

    # 使用更小的批次大小来模拟持续并发
    local batch_size=$CONCURRENCY
    local num_batches=$(( (REQUESTS + batch_size - 1) / batch_size ))

    for ((batch=0; batch<num_batches; batch++)); do
        local batch_start=$((batch * batch_size + 1))
        local batch_end=$(( (batch + 1) * batch_size ))

        if [ "$batch_end" -gt "$REQUESTS" ]; then
            batch_end="$REQUESTS"
        fi

        CURRENT_BATCH=$batch_start

        # 发送批次请求
        for ((i=batch_start; i<=batch_end; i++)); do
            send_single_request "$i" &
        done

        # 对于非最后一组,短暂等待后继续下一批,以模拟持续并发
        if [ $batch -lt $((num_batches - 1)) ]; then
            sleep 0.01  # 10ms延迟,减少批次间的等待时间
        fi
    done

    # 等待所有请求完成
    wait

    # 显示最终统计
    show_realtime_stats "$REQUESTS" "$REQUESTS"

    # 最终报告
    show_detailed_report "$REQUESTS" "$REQUESTS" "true"

    print_info "测试完成!"
    echo ""
}

# 计算最终统计
calculate_final_stats() {
    local end_time=$(date +%s%N)
    local total_time_ns=$((end_time - TOTAL_START_TIME))
    local total_time_ms=$((total_time_ns / 1000000))
    local total_time_sec=$((total_time_ms / 1000))

    # 计算QPS
    local qps=0
    if [ "$total_time_sec" -gt 0 ]; then
        qps=$((REQUESTS / total_time_sec))
    fi

    # 计算精确QPS(保留一位小数)
    local precise_qps=0
    local precise_qps_decimal=0
    if [ "$total_time_ms" -gt 0 ]; then
        precise_qps=$((REQUESTS * 1000 / total_time_ms))
        precise_qps_decimal=$(( (REQUESTS * 1000 * 10 / total_time_ms) % 10 ))
    fi

    # 计算响应时间统计
    local min_time=9999999
    local max_time=0
    local total_response_time=0

    for time in "${RESPONSE_TIMES[@]}"; do
        if [ "$time" -lt "$min_time" ] && [ "$time" -gt 0 ]; then
            min_time="$time"
        fi
        if [ "$time" -gt "$max_time" ]; then
            max_time="$time"
        fi
        total_response_time=$((total_response_time + time))
    done

    local avg_response_time=0
    if [ ${#RESPONSE_TIMES[@]} -gt 0 ]; then
        avg_response_time=$((total_response_time / ${#RESPONSE_TIMES[@]}))
    fi

    # 统计状态码
    declare -A status_count
    for code in "${STATUS_CODES[@]}"; do
        ((status_count[$code]++))
    done

    # 输出最终结果
    echo -e "${CYAN}════════════════════════════════════════${NC}"
    echo -e "${WHITE}            最终测试结果${NC}"
    echo -e "${CYAN}════════════════════════════════════════${NC}"
    echo -e "${BLUE}测试时间:${NC}     $(date '+%Y-%m-%d %H:%M:%S')"
    echo -e "${BLUE}目标URL:${NC}      $URL"
    echo -e "${BLUE}HTTP方法:${NC}     $METHOD"
    echo -e "${BLUE}总请求数:${NC}     $REQUESTS"
    echo -e "${BLUE}并发数:${NC}       $CONCURRENCY"
    echo -e "${CYAN}----------------------------------------${NC}"
    echo -e "${GREEN}总耗时:${NC}       ${total_time_sec}.$((total_time_ms % 1000)) 秒"
    echo -e "${GREEN}平均QPS:${NC}      ${precise_qps}.${precise_qps_decimal} 请求/秒"
    echo -e "${GREEN}成功请求:${NC}     $TOTAL_SUCCESS"
    echo -e "${RED}失败请求:${NC}     $TOTAL_FAILED"
    echo -e "${YELLOW}超时请求:${NC}     $TOTAL_TIMEOUT"
    echo -e "${BLUE}成功率:${NC}       $((TOTAL_SUCCESS * 100 / REQUESTS))%"
    echo -e "${CYAN}----------------------------------------${NC}"
    echo -e "${BLUE}响应时间统计:${NC}"
    echo -e "  平均: ${avg_response_time}ms"
    echo -e "  最小: ${min_time}ms"
    echo -e "  最大: ${max_time}ms"

    # 响应时间分布
    if [ ${#RESPONSE_TIMES[@]} -gt 0 ]; then
        echo -e "${CYAN}----------------------------------------${NC}"
        echo -e "${BLUE}响应时间分布:${NC}"

        local ranges=("0-50ms" "51-100ms" "101-200ms" "201-500ms" "501-1000ms" ">1000ms")
        local range_counts=(0 0 0 0 0 0)

        for time in "${RESPONSE_TIMES[@]}"; do
            if [ "$time" -le 50 ]; then
                ((range_counts[0]++))
            elif [ "$time" -le 100 ]; then
                ((range_counts[1]++))
            elif [ "$time" -le 200 ]; then
                ((range_counts[2]++))
            elif [ "$time" -le 500 ]; then
                ((range_counts[3]++))
            elif [ "$time" -le 1000 ]; then
                ((range_counts[4]++))
            else
                ((range_counts[5]++))
            fi
        done

        for i in "${!ranges[@]}"; do
            local count=${range_counts[$i]}
            local percent=0
            if [ ${#RESPONSE_TIMES[@]} -gt 0 ]; then
                percent=$((count * 100 / ${#RESPONSE_TIMES[@]}))
            fi
            local bar_length=$((percent / 2))
            local bar=$(printf "%${bar_length}s" | tr ' ' '█')
            printf "  %-10s: %4d (%3d%%) %s\n" "${ranges[$i]}" "$count" "$percent" "$bar"
        done
    fi

    # 状态码分布
    if [ ${#STATUS_CODES[@]} -gt 0 ]; then
        echo -e "${CYAN}----------------------------------------${NC}"
        echo -e "${BLUE}HTTP状态码分布:${NC}"

        for code in $(printf "%s\n" "${!status_count[@]}" | sort -n); do
            local count=${status_count[$code]}
            local percent=$((count * 100 / ${#STATUS_CODES[@]}))
            local bar_length=$((percent / 2))
            local bar=$(printf "%${bar_length}s" | tr ' ' '█')

            local color="$GREEN"
            if [[ "$code" =~ ^4[0-9][0-9]$ ]]; then
                color="$YELLOW"
            elif [[ "$code" =~ ^5[0-9][0-9]$ ]]; then
                color="$RED"
            fi

            printf "  ${color}%-10s${NC}: %4d (%3d%%) %s\n" "$code" "$count" "$percent" "$bar"
        done
    fi

    # 性能评级
    echo -e "${CYAN}----------------------------------------${NC}"
    echo -e "${BLUE}性能评级:${NC}"

    if [ $precise_qps -ge 1000 ]; then
        echo -e "  ${GREEN}优秀 (>1000 QPS)${NC} - 性能极佳"
    elif [ $precise_qps -ge 500 ]; then
        echo -e "  ${GREEN}良好 (500-1000 QPS)${NC} - 性能良好"
    elif [ $precise_qps -ge 100 ]; then
        echo -e "  ${YELLOW}一般 (100-500 QPS)${NC} - 性能一般"
    elif [ $precise_qps -ge 10 ]; then
        echo -e "  ${YELLOW}较差 (10-100 QPS)${NC} - 性能较差"
    else
        echo -e "  ${RED}极差 (<10 QPS)${NC} - 性能需要优化"
    fi

    echo -e "${CYAN}════════════════════════════════════════${NC}"

    # 输出到文件
    if [ -n "$OUTPUT_FILE" ]; then
        {
            echo "测试时间: $(date '+%Y-%m-%d %H:%M:%S')"
            echo "目标URL: $URL"
            echo "总请求数: $REQUESTS"
            echo "并发数: $CONCURRENCY"
            echo "总耗时: ${total_time_sec}.$((total_time_ms % 1000)) 秒"
            echo "QPS: ${precise_qps}.${precise_qps_decimal}"
            echo "成功请求: $TOTAL_SUCCESS"
            echo "失败请求: $TOTAL_FAILED"
            echo "超时请求: $TOTAL_TIMEOUT"
            echo "成功率: $((TOTAL_SUCCESS * 100 / REQUESTS))%"
            echo "平均响应时间: ${avg_response_time}ms"
            echo "最小响应时间: ${min_time}ms"
            echo "最大响应时间: ${max_time}ms"
        } > "$OUTPUT_FILE"
        print_info "结果已保存到: $OUTPUT_FILE"
    fi
}

# 主函数
main() {
    # 检查依赖
    check_dependencies

    # 解析参数
    parse_args "$@"

    # 运行测试
    run_test

    # 计算并显示最终统计
    calculate_final_stats
}

# 运行主函数
main "$@"
上一篇 下一篇

猜你喜欢

热点阅读