From 7702fa669630fa6ae0b7186c80a6d2c92e33bf2c Mon Sep 17 00:00:00 2001 From: yusing Date: Sat, 3 Jan 2026 00:28:59 +0800 Subject: [PATCH] feat(benchmark): enhance dev.compose.yml with benchmark services and scripts - Added benchmark services (whoami, godoxy, traefik, caddy, nginx) to dev.compose.yml. - Introduced a new benchmark.sh script for load testing using wrk and h2load. - Updated Makefile to include a benchmark target for easy execution of the new script. --- Makefile | 7 +- dev.compose.yml | 146 +++++++++++++++++++++++++++++++++++ scripts/benchmark.sh | 179 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 331 insertions(+), 1 deletion(-) create mode 100644 scripts/benchmark.sh diff --git a/Makefile b/Makefile index 785a5f75..32209f32 100755 --- a/Makefile +++ b/Makefile @@ -123,6 +123,11 @@ dev: dev-build: build docker compose -f dev.compose.yml up -t 0 -d app --force-recreate +benchmark: + @docker compose -f dev.compose.yml up -d --force-recreate whoami godoxy traefik caddy nginx + sleep 1 + @./scripts/benchmark.sh + dev-run: build cd dev-data && ${BIN_PATH} @@ -142,7 +147,7 @@ ci-test: act -n --artifact-server-path /tmp/artifacts -s GITHUB_TOKEN="$$(gh auth token)" cloc: - scc -w -i go --not-match '_test.go$' + scc -w -i go --not-match '_test.go$$' push-github: git push origin $(shell git rev-parse --abbrev-ref HEAD) diff --git a/dev.compose.yml b/dev.compose.yml index 21704d63..4e71291a 100644 --- a/dev.compose.yml +++ b/dev.compose.yml @@ -1,3 +1,8 @@ +x-benchmark: &benchmark + restart: no + labels: + proxy.exclude: true + proxy.#1.healthcheck.disable: true services: app: image: godoxy-dev @@ -90,7 +95,148 @@ services: labels: proxy.#1.scheme: h2c proxy.#1.port: 80 + whoami: + <<: *benchmark + image: traefik/whoami:latest + container_name: whoami + godoxy: + <<: *benchmark + build: . + container_name: godoxy-benchmark + environment: + DOCKER_HOST: unix:///var/run/docker.sock + ports: + - 8080:80 + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + configs: + - source: godoxy_config + target: /app/config/config.yml + - source: godoxy_provider + target: /app/config/providers.yml + traefik: + <<: *benchmark + image: traefik:latest + container_name: traefik + command: + - --api.insecure=true + - --entrypoints.web.address=:8081 + - --providers.file.directory=/etc/traefik/dynamic + - --providers.file.watch=true + - --log.level=ERROR + ports: + - 8081:8081 + configs: + - source: traefik_config + target: /etc/traefik/dynamic/routes.yml + caddy: + <<: *benchmark + image: caddy:latest + container_name: caddy + ports: + - 8082:80 + configs: + - source: caddy_config + target: /etc/caddy/Caddyfile + tmpfs: + - /data + - /config + nginx: + <<: *benchmark + image: nginx:latest + container_name: nginx + command: nginx -g 'daemon off;' -c /etc/nginx/nginx.conf + ports: + - 8083:80 + configs: + - source: nginx_config + target: /etc/nginx/nginx.conf + configs: + godoxy_config: + content: | + providers: + include: + - providers.yml + godoxy_provider: + content: | + whoami: + host: whoami + traefik_config: + content: | + http: + routers: + whoami: + rule: "Host(`whoami.domain.com`)" + entryPoints: + - web + service: whoami + services: + whoami: + loadBalancer: + servers: + - url: "http://whoami:80" + caddy_config: + content: | + { + admin off + auto_https off + default_bind 0.0.0.0 + + servers { + protocols h2c h1 + } + } + + http://whoami.domain.com:80 { + reverse_proxy whoami:80 + } + nginx_config: + content: | + worker_processes auto; + worker_rlimit_nofile 65535; + error_log /dev/null; + pid /var/run/nginx.pid; + + events { + worker_connections 10240; + multi_accept on; + use epoll; + } + + http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + access_log off; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + keepalive_requests 10000; + + upstream backend { + server whoami:80; + keepalive 128; + } + + server { + listen 80; + server_name whoami.domain.com; + http2 on; + + location / { + proxy_pass http://backend; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_set_header Host $$host; + proxy_set_header X-Real-IP $$remote_addr; + proxy_set_header X-Forwarded-For $$proxy_add_x_forwarded_for; + proxy_buffering off; + } + } + } parca: content: | object_storage: diff --git a/scripts/benchmark.sh b/scripts/benchmark.sh new file mode 100644 index 00000000..77a2bbe8 --- /dev/null +++ b/scripts/benchmark.sh @@ -0,0 +1,179 @@ +#!/bin/bash +# Benchmark script to compare GoDoxy, Traefik, Caddy, and Nginx +# Uses wrk for HTTP load testing + +set -e + +# Configuration +HOST="whoami.domain.com" +DURATION="10s" +THREADS=4 +CONNECTIONS=100 + +# Color functions for output +red() { echo -e "\033[0;31m$*\033[0m"; } +green() { echo -e "\033[0;32m$*\033[0m"; } +yellow() { echo -e "\033[1;33m$*\033[0m"; } +blue() { echo -e "\033[0;34m$*\033[0m"; } + +# Check if wrk is installed +if ! command -v wrk &> /dev/null; then + red "Error: wrk is not installed" + echo "Please install wrk:" + echo " Ubuntu/Debian: sudo apt-get install wrk" + echo " macOS: brew install wrk" + echo " Or build from source: https://github.com/wg/wrk" + exit 1 +fi + +if ! command -v h2load &> /dev/null; then + red "Error: h2load is not installed" + echo "Please install h2load (nghttp2-client):" + echo " Ubuntu/Debian: sudo apt-get install nghttp2-client" + echo " macOS: brew install nghttp2" + exit 1 +fi + +OUTFILE="/tmp/reverse_proxy_benchmark_$(date +%Y%m%d_%H%M%S).log" +: > "$OUTFILE" +exec > >(tee -a "$OUTFILE") 2>&1 + +blue "========================================" +blue "Reverse Proxy Benchmark Comparison" +blue "========================================" +echo "" +echo "Target: $HOST" +echo "Duration: $DURATION" +echo "Threads: $THREADS" +echo "Connections: $CONNECTIONS" +echo "" + +# Define services to test +declare -A services=( + ["GoDoxy"]="http://127.0.0.1:8080/bench" + ["Traefik"]="http://127.0.0.1:8081/bench" + ["Caddy"]="http://127.0.0.1:8082/bench" + ["Nginx"]="http://127.0.0.1:8083/bench" +) + +# Array to store connection errors +declare -a connection_errors=() + +# Function to test connection before benchmarking +test_connection() { + local name=$1 + local url=$2 + + yellow "Testing connection to $name..." + + # Test HTTP/1.1 + local res1=$(curl -sS -w "\n%{http_code}" --http1.1 -H "Host: $HOST" --max-time 5 "$url") + local body1=$(echo "$res1" | head -n -1) + local status1=$(echo "$res1" | tail -n 1) + + # Test HTTP/2 + local res2=$(curl -sS -w "\n%{http_code}" --http2-prior-knowledge -H "Host: $HOST" --max-time 5 "$url") + local body2=$(echo "$res2" | head -n -1) + local status2=$(echo "$res2" | tail -n 1) + + local failed=false + if [ "$status1" != "200" ] || [ "$body1" != "1" ]; then + red "✗ $name failed HTTP/1.1 connection test (Status: $status1, Body: $body1)" + failed=true + fi + + if [ "$status2" != "200" ] || [ "$body2" != "1" ]; then + red "✗ $name failed HTTP/2 connection test (Status: $status2, Body: $body2)" + failed=true + fi + + if [ "$failed" = true ]; then + connection_errors+=("$name failed connection test (URL: $url)") + return 1 + else + green "✓ $name is reachable (HTTP/1.1 & HTTP/2)" + return 0 + fi +} + +blue "========================================" +blue "Connection Tests" +blue "========================================" +echo "" + +# Run connection tests for all services +for name in "${!services[@]}"; do + test_connection "$name" "${services[$name]}" +done + +echo "" +blue "========================================" + +# Exit if any connection test failed +if [ ${#connection_errors[@]} -gt 0 ]; then + echo "" + red "Connection test failed for the following services:" + for error in "${connection_errors[@]}"; do + red " - $error" + done + echo "" + red "Please ensure all services are running before benchmarking" + exit 1 +fi + +echo "" +green "All services are reachable. Starting benchmarks..." +echo "" +blue "========================================" +echo "" + +# Function to run benchmark +run_benchmark() { + local name=$1 + local url=$2 + local h2_duration="${DURATION%s}" + + yellow "Testing $name..." + + echo "========================================" + echo "$name" + echo "URL: $url" + echo "========================================" + echo "" + echo "[HTTP/1.1] wrk" + + wrk -t"$THREADS" -c"$CONNECTIONS" -d"$DURATION" \ + -H "Host: $HOST" \ + "$url" + + echo "" + echo "[HTTP/2] h2load" + + h2load -t"$THREADS" -c"$CONNECTIONS" --duration="$h2_duration" \ + -H "Host: $HOST" \ + -H ":authority: $HOST" \ + "$url" | grep -vE "^(starting benchmark...|spawning thread #|progress: |Warm-up )" + + echo "" + green "✓ $name benchmark completed" + blue "----------------------------------------" + echo "" +} + +# Run benchmarks for each service +for name in "${!services[@]}"; do + run_benchmark "$name" "${services[$name]}" +done + +blue "========================================" +blue "Benchmark Summary" +blue "========================================" +echo "" +echo "All benchmark output saved to: $OUTFILE" +echo "" +echo "Key metrics to compare:" +echo " - Requests/sec (throughput)" +echo " - Latency (mean, stdev)" +echo " - Transfer/sec" +echo "" +green "All benchmarks completed!"