vegeta压测p95/p99不准的常见原因包括:-rate=0导致调度延迟干扰、未设-timeout剔除超时长尾、误读mean掩盖异常;需固定速率+显式超时+json提取。

vegeta 压测时 P95/P99 延迟不准的常见原因
vegeta 默认输出的 latencies.p95 和 latencies.p99 是可信的,但前提是命令参数没踩坑。很多人看到 P99 比预期低,其实是压测本身失真了。
-
-rate=0会启用“尽最大努力发送”,实际请求被排队调度,测出的是 goroutine 调度延迟,不是接口真实响应延迟 - 没设
-timeout:超时请求计入错误率,但不参与延迟统计——P99 只从成功请求里算,长尾被悄悄剔除 - 直接读
mean字段:它对异常值极不敏感,200ms 平均延迟下可能藏着 2s 的 P99,完全掩盖问题 - 正确做法是固定速率 + 显式超时 + 提取 JSON 字段:
vegeta report -type=json results.bin | jq '.latencies.p95'
hey/wrk 测出 QPS 虚高,怎么验证是不是连接复用干扰
Go 默认 http.DefaultClient 复用 TCP 连接,而 Gin/Echo 等框架在复用连接下会绕过连接池瓶颈,导致 QPS 数值好看但不反映真实承载力。
- 用
hey时加-H "Connection: close"强制短连接,暴露底层连接池压力 - 用
vegeta时配-http2=false并自定义 transport:http.Transport.MaxIdleConns = 0和MaxIdleConnsPerHost = 0 - gRPC 接口必须禁用 HTTP/2 流复用:
grpc.WithTransportCredentials(insecure.NewCredentials()),否则单连接吞吐虚高 - 观察服务端
netstat -an | grep :8080 | wc -l,短连接下 ESTABLISHED 数应接近并发数;若远低于并发数,说明复用未关闭
pprof 抓不到真实瓶颈?三个关键采样条件
压测中跑 go tool pprof 却看不到热点函数,大概率是采集方式错了。pprof 不是开关一开就有效,它依赖足够长、足够稳的样本。
- CPU profile 必须 ≥ 30 秒:
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30,太短采样点不足,runtime.findrunnable这类调度热点根本显不出来 - 堆内存 profile 要加
?gc=1:http://localhost:6060/debug/pprof/heap?gc=1,否则看到的是累计分配量,不是当前存活对象,内存泄漏会被掩盖 - goroutine profile 必须带
?debug=2:http://localhost:6060/debug/pprof/goroutine?debug=2,否则卡在chan receive或select的泄漏 goroutine 不显示完整栈 - 压测流量要稳:用
hey -z 60s -c 50而不是-n 10000,避免脉冲式请求导致 profile 失真
容器环境压测结果忽高忽低,先查 GOMAXPROCS 和 GOGC
Kubernetes 默认不设 resources.limits.cpu,runtime.NumCPU() 返回 1,GOMAXPROCS 就锁死为 1,所有 goroutine 被挤在单个 P 上跑,QPS 上不去还 CPU 100%。
- 压测前必须显式设置:
GOMAXPROCS=4 GOGC=200 go run main.go(根据宿主机核数调整) - 用
go tool pprof http://localhost:6060/debug/pprof/gc查 GC 频次:30 秒内 ≥ 5 次,说明 GC 成瓶颈,不是业务逻辑慢 - 容器里不设
resources.limits.cpu,docker stats看到的 CPU 利用率永远卡在 100%,但实际只用了 1 个核 -
sync.runtime_SemacquireMutex在 top 列表高频出现?检查热路径是否误用了Lock()而非RUnlock(),尤其全局 map 读多写少场景
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











