压力测试
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 "$@"