开源项目 API 性能文档自动化:用 wrk 和 k6 生成可复现的压测报告
一、手工压测的三个痛点
压测自动化的核心是将"一次性手工操作"转化为"可复现的工程流程"。以下流程展示了从压测脚本到 CI 集成再到报告生成的完整闭环:
一、手工压测的三个痛点
开源项目的性能数据往往来自开发者的口述:"这个接口很快,5000 QPS 没问题"。但这种说辞不可复现、不可验证。社区贡献者优化代码后,无法判断改进是否真实有效。
将压测脚本纳入 CI 和文档,解决三个问题:
- 性能声明有据可查(每次发版都跑压测,生成报告)
- 贡献者可以本地跑压测验证自己的优化
- 性能劣化在 CI 中自动发现
二、wrk 的自动化压测框架
-- benchmark.lua: wrk 脚本,模拟真实 API 请求 wrk.method = "POST" wrk.body = '{"prompt": "hello world", "max_tokens": 100}' wrk.headers["Content-Type"] = "application/json" wrk.headers["Authorization"] = "Bearer test-token" -- 随机化请求体(避免缓存效应) request = function() local prompts = { "explain Go concurrency", "write a Python decorator", "optimize this SQL query", } local idx = math.random(#prompts) wrk.body = string.format( '{"prompt": "%s", "max_tokens": %d}', prompts[idx], math.random(50, 200) ) return wrk.format() end -- 自定义响应解析 response = function(status, headers, body) if status ~= 200 then io.stderr:write(string.format("Error: %d - %s\n", status, body)) end end#!/bin/bash # scripts/bench.sh - 可复现的压测脚本 set -euo pipefail ENDPOINT="${1:-http://localhost:8080}" DURATION="${2:-30s}" CONNECTIONS="${3:-100}" THREADS="${4:-4}" echo "=== API 性能压测报告 ===" echo "端点: $ENDPOINT" echo "持续时间: $DURATION" echo "连接数: $CONNECTIONS" echo "时间: $(date -Iseconds)" echo "" # 预热(消除冷启动影响) echo "[1/3] 预热中..." wrk -t2 -c10 -d10s --latency "$ENDPOINT/api/health" > /dev/null # 核心接口压测 echo "[2/3] 压测核心接口..." endpoints=( "/api/chat/completions" "/api/embeddings" ) for ep in "${endpoints[@]}"; do echo "--- $ep ---" wrk -t"$THREADS" -c"$CONNECTIONS" -d"$DURATION" \ --latency \ -s benchmark.lua \ "$ENDPOINT$ep" | tee "reports/bench_$(echo $ep | tr '/' '_').txt" echo "" done echo "[3/3] 报告已保存到 reports/ 目录"2.1 k6 的高级压测场景
// k6-benchmark.js - 模拟真实用户行为的压测脚本 import http from 'k6/http'; import { check, sleep, group } from 'k6'; import { Rate, Trend } from 'k6/metrics'; const errorRate = new Rate('errors'); const chatLatency = new Trend('chat_latency'); export const options = { stages: [ { duration: '30s', target: 20 }, // 爬坡 { duration: '1m', target: 100 }, // 稳态 { duration: '30s', target: 0 }, // 退坡 ], thresholds: { http_req_duration: ['p(95)<500'], // 95% 请求 < 500ms errors: ['rate<0.01'], // 错误率 < 1% }, }; export default function () { group('Chat Completion', () => { const payload = JSON.stringify({ model: 'gpt-4-turbo', messages: [{ role: 'user', content: 'hello' }], max_tokens: 50, }); const res = http.post('http://localhost:8080/api/chat', payload, { headers: { 'Content-Type': 'application/json' }, }); const success = check(res, { 'status is 200': (r) => r.status === 200, 'response has choices': (r) => r.json('choices').length > 0, }); errorRate.add(!success); chatLatency.add(res.timings.duration); }); sleep(1); // 模拟用户间隔 }# CI 集成:在 GitHub Actions 中运行 k6 压测 k6 run --out json=reports/k6-results.json k6-benchmark.js # 生成 HTML 报告 k6 run --out dashboard=reports/k6-report.html k6-benchmark.js三、性能报告自动生成
# API 性能报告 (自动生成) **生成时间**: 2026-07-17 10:00 UTC **测试环境**: t3.medium (2C4G), Ubuntu 22.04 **提交**: abc1234 ## Chat Completion API | 指标 | 值 | |------|-----| | QPS | 842 req/s | | P50 延迟 | 45ms | | P95 延迟 | 128ms | | P99 延迟 | 210ms | | 错误率 | 0.02% | ## 历史趋势 | 版本 | QPS | P95 延迟 | 对比 | |------|-----|---------|------| | v2.3.1 | 842 | 128ms | 基准 | | v2.3.0 | 810 | 142ms | +4% QPS, -10% P95 | | v2.2.0 | 780 | 155ms | +8% QPS, -17% P95 |四、边界与权衡
压测对生产的影响:在 CI 中压测的是 staging 环境,不要对生产环境做压力测试。如果必须测试生产级性能,使用影子流量(复制生产流量的 1%)重放到测试环境。
压测的"实验室效应":wrk/k6 压测的请求模式与真实用户不同——真实用户有 think time、网络波动、设备差异。压测结果应该作为性能基线,而非"用户能体验到的确切数字"。
基准环境的稳定性:GitHub Actions Runner 的 CPU 型号不同可能导致 10-15% 的性能波动。对发布版本的基准测试建议在固定物理机或专用 Runner 上运行。
五、总结
开源项目的性能文档自动化用 wrk 做快速验证、k6 做真实场景模拟、CI 做自动化回归检测。核心是将压测从"一次性的手工操作"变成"可复现的工程流程"。
实施建议:先写一个 30 秒的 wrk 脚本验证核心接口的 QPS 基线 → 集成到 CI 中(每次发版自动跑) → 用 k6 补充多场景模拟。压测的目标不是"压出最高数字"而是"发现性能劣化"——每次发版的压测结果与上一个版本对比,劣化 > 10% 触发告警。