go test -bench 不是压测工具,仅适用于纯内存操作的组件级微基准测试;它串行执行、禁用GC、忽略网络/I/O瓶颈,无法反映真实并发请求下的QPS、延迟分布等关键指标。

Go 的 go test -bench 不是压力测试工具,强行当压测脚本用会严重误导性能结论。它专为测量单个函数在受控、无干扰环境下的纳秒级开销设计,和真实并发请求场景(网络延迟、连接复用、资源争用)有本质区别。
为什么 Benchmark 函数不能直接当压测脚本用
Go 的基准测试运行时强制串行执行、禁用 GC(或大幅减少触发频率)、忽略 OS 调度抖动,并反复调用同一函数多次取均值。这导致:
-
BenchmarkFoo(b *testing.B)测的是“理想内存中函数调用”,不是“100 个 goroutine 持续发 HTTP 请求” - 它不模拟连接池耗尽、上下文超时、错误重试等关键路径,
b.N增长只放大 CPU 单点瓶颈,掩盖 I/O 瓶颈 - 结果单位是 ns/op,无法换算成 QPS/TPS;你看到
124 ns/op,但实际服务可能只有 800 QPS - 所有
testing.B方法(如b.ReportAllocs())只统计被测函数内部的内存分配,不包含 net/http 底层 buffer、TLS handshake 等真实开销
真正要压测 HTTP 服务,该用什么
必须切换到面向真实请求流的工具链。推荐组合:
- 用
hey(Go 写的轻量压测器):支持 HTTP/1.1、HTTP/2、自定义 header、QPS 限速,输出含 P90/P99、错误率、吞吐图表 - 用
vegeta(更灵活):支持 JSON 输入、动态负载曲线(ramp-up)、导出 CSV/JSON 结果,适合 CI 集成 - 自己写压测逻辑?用
net/http+sync.WaitGroup+time.AfterFunc控制持续时间,但务必:
– 复用&http.Client{Transport: &http.Transport{MaxIdleConns: 100, MaxIdleConnsPerHost: 100}}
– 用context.WithTimeout包裹每个请求
– 单独 goroutine 统计成功/失败/延迟直方图,避免锁争用
go test -bench 还值得写吗?怎么写才不白费功夫
值得,但只用于「组件级微基准」:比如验证一个新写的 JSON 解析器比 encoding/json 快多少,或比较两种 map 查找策略。关键约束:
- 被测函数必须是纯内存操作,零外部依赖(不碰网络、磁盘、全局变量、time.Now())
- 提前在
Benchmark函数开头调用b.ResetTimer(),把初始化(如构建测试数据)排除在计时外 - 避免在循环内做非必要分配:不要在
for i := 0; i 里 new struct,改用预分配 slice 复用 - 加
b.RunParallel测试并发安全性和扩展性,例如:func BenchmarkMapConcurrent(b *testing.B) { m := sync.Map{} b.RunParallel(func(pb *testing.PB) { for pb.Next() { m.Store(rand.Intn(1000), rand.Intn(1000)) } }) }
真正难的不是写压测代码,而是定义清楚你要回答的问题:是“这个算法快不快”,还是“线上服务扛不扛得住”。前者用 go test -bench,后者必须脱离 testing 包,直面连接、超时、错误、监控的真实世界。











