95 行
3.1 KiB
Bash
95 行
3.1 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
|
|
. "$ROOT_DIR/scripts/lib.sh"
|
||
|
|
load_env
|
||
|
|
|
||
|
|
PROFILE="${1:-base}"
|
||
|
|
DURATION="${2:-30}"
|
||
|
|
REPORT_FILE="$ROOT_DIR/dist/bench-$(date +%Y%m%d%H%M%S).json"
|
||
|
|
mkdir -p "$ROOT_DIR/dist"
|
||
|
|
|
||
|
|
audit "bench" "STARTED" "profile=$PROFILE duration=${DURATION}s"
|
||
|
|
progress "bench" "STARTED" "profile=$PROFILE"
|
||
|
|
|
||
|
|
require_cmd curl
|
||
|
|
require_cmd docker
|
||
|
|
|
||
|
|
BASE_URL="${CONSOLE_DOMAIN:-http://localhost}"
|
||
|
|
|
||
|
|
bench_http() {
|
||
|
|
local name="$1"
|
||
|
|
local url="$2"
|
||
|
|
local concurrency="${3:-10}"
|
||
|
|
local requests="${4:-100}"
|
||
|
|
local pass_rps="${5:-50}"
|
||
|
|
local pass_p99_ms="${6:-500}"
|
||
|
|
|
||
|
|
printf 'Benchmarking %s (%s)...\n' "$name" "$url"
|
||
|
|
|
||
|
|
if command -v ab >/dev/null 2>&1; then
|
||
|
|
RAW="$(ab -n "$requests" -c "$concurrency" -q "$url" 2>/dev/null || true)"
|
||
|
|
RPS="$(printf '%s' "$RAW" | grep 'Requests per second' | awk '{print $4}' | cut -d. -f1)"
|
||
|
|
P99="$(printf '%s' "$RAW" | grep '99%' | awk '{print $2}')"
|
||
|
|
RPS="${RPS:-0}"
|
||
|
|
P99="${P99:-9999}"
|
||
|
|
elif command -v wrk >/dev/null 2>&1; then
|
||
|
|
RAW="$(wrk -t2 -c"$concurrency" -d"${DURATION}s" "$url" 2>/dev/null || true)"
|
||
|
|
RPS="$(printf '%s' "$RAW" | grep 'Requests/sec' | awk '{print $2}' | cut -d. -f1)"
|
||
|
|
P99="${P99:-0}"
|
||
|
|
RPS="${RPS:-0}"
|
||
|
|
else
|
||
|
|
# Fallback: sequential curl timing
|
||
|
|
total_ms=0
|
||
|
|
for _ in $(seq 1 20); do
|
||
|
|
ms="$(curl -sk -o /dev/null -w '%{time_total}' --max-time 5 "$url" 2>/dev/null | awk '{printf "%d", $1*1000}')"
|
||
|
|
total_ms=$((total_ms + ms))
|
||
|
|
done
|
||
|
|
AVG_MS=$((total_ms / 20))
|
||
|
|
RPS=$((1000 / (AVG_MS + 1)))
|
||
|
|
P99="$AVG_MS"
|
||
|
|
fi
|
||
|
|
|
||
|
|
PASS="true"
|
||
|
|
[ "$RPS" -lt "$pass_rps" ] && PASS="false"
|
||
|
|
[ "$P99" -gt "$pass_p99_ms" ] && PASS="false"
|
||
|
|
|
||
|
|
printf ' RPS: %s (min %s) | P99: %sms (max %sms) | %s\n' \
|
||
|
|
"$RPS" "$pass_rps" "$P99" "$pass_p99_ms" "$( [ "$PASS" = "true" ] && printf 'PASS' || printf 'FAIL')"
|
||
|
|
|
||
|
|
printf '{"name":"%s","url":"%s","rps":%s,"p99_ms":%s,"target_rps":%s,"target_p99_ms":%s,"pass":%s}' \
|
||
|
|
"$name" "$url" "$RPS" "$P99" "$pass_rps" "$pass_p99_ms" "$PASS"
|
||
|
|
}
|
||
|
|
|
||
|
|
RESULTS=()
|
||
|
|
|
||
|
|
RESULTS+=("$(bench_http "tenant-api-health" "$BASE_URL/actuator/health" 5 50 20 1000)")
|
||
|
|
|
||
|
|
if printf '%s' "$PROFILE" | grep -q 'im' && [ -n "${IM_DOMAIN:-}" ]; then
|
||
|
|
RESULTS+=("$(bench_http "im-api-health" "${IM_DOMAIN}/actuator/health" 5 50 20 1000)")
|
||
|
|
fi
|
||
|
|
if printf '%s' "$PROFILE" | grep -q 'update' && [ -n "${UPDATE_DOMAIN:-}" ]; then
|
||
|
|
RESULTS+=("$(bench_http "update-api-health" "${UPDATE_DOMAIN}/actuator/health" 5 50 20 1000)")
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Docker stats snapshot
|
||
|
|
DOCKER_STATS="$(docker stats --no-stream --format '{"container":"{{.Name}}","cpu":"{{.CPUPerc}}","mem":"{{.MemUsage}}"}' 2>/dev/null || echo '[]')"
|
||
|
|
|
||
|
|
RESULTS_JSON="$(printf '%s\n' "${RESULTS[@]}" | paste -sd ',' - | sed 's/^/[/' | sed 's/$/]/')"
|
||
|
|
|
||
|
|
cat > "$REPORT_FILE" <<EOF
|
||
|
|
{
|
||
|
|
"timestamp": "$(now)",
|
||
|
|
"profile": "$PROFILE",
|
||
|
|
"durationSec": $DURATION,
|
||
|
|
"privateVersion": "${PRIVATE_VERSION:-unknown}",
|
||
|
|
"results": $RESULTS_JSON,
|
||
|
|
"dockerStats": $DOCKER_STATS
|
||
|
|
}
|
||
|
|
EOF
|
||
|
|
|
||
|
|
audit "bench" "DONE" "report=$REPORT_FILE"
|
||
|
|
progress "bench" "DONE" "profile=$PROFILE"
|
||
|
|
printf 'Benchmark report: %s\n' "$REPORT_FILE"
|