go test -bench=. 不适合测微服务吞吐量,因其绕过网络栈仅测试内存中 handler 性能;真实压测需用 vegeta/wrk(禁 keep-alive)、ghz(grpc),并严格控制环境、采集 pprof、清缓存与重启服务。

别用 go test -bench=. 测微服务吞吐量——它根本没走网络栈,测的不是你的服务,是 handler 函数在内存里跑多快。
go test -bench 只适合测纯逻辑,必须禁日志、mock 外部依赖
它能验证路由匹配、中间件顺序、JSON 序列化这些内存操作是否高效,但绕过了 TCP、TLS、连接池、accept 队列等真实瓶颈点。一旦 handler 里有 log.Printf 或真实调用 http.Get,结果就完全失真。
- 开头加
log.SetOutput(io.Discard),否则日志 I/O 拖慢几十倍 -
req := httptest.NewRequest(...)和w := httptest.NewRecorder()必须放在循环外,避免重复分配 - 调用 handler 时直接写
handler(w, req),别用http.HandlerFunc(handler).ServeHTTP(w, req)——后者带反射开销 - 所有外部依赖(DB、Redis、gRPC)必须 mock;全局缓存 map 初始化要包在
b.StopTimer()里 - 务必调用
b.ResetTimer(),否则路由注册、中间件构造的耗时全算进结果
真实链路压测必须禁 Keep-Alive,用 vegeta 或 wrk
连接复用会掩盖服务端连接池打满、TIME_WAIT 溢出、accept 队列阻塞这些关键问题。你不是在测“能发多快”,而是在测“服务端会不会卡死”。
-
wrk命令要加-H "Connection: close":wrk -c 200 -d 30s -t 4 --timeout 5s -H "Connection: close" http://localhost:8080/api/users -
vegeta更适合带 body 和 header 的场景:echo "POST http://localhost:8080/api/login" | vegeta attack -body login.json -header "Content-Type: application/json" -rate 100 -duration 30s -timeout 5s | vegeta report - 压测机和服务端必须物理或网络隔离,避免 CPU/网卡争抢干扰
- 并发数
-c和单 worker QPS-q要配平,比如想稳压 300 QPS,用-c 150 -q 2,而不是-c 1000 -q 0导致本机端口耗尽
gRPC 微服务别手写 goroutine,必须用 ghz
手写容易误用 grpc.Dial(每 goroutine 新建连接 → 打爆服务端 fd)、漏设 context.WithTimeout(统计失真)、忽略 WithBlock()(阻塞在 DNS 或连接建立上)。
- 推荐命令:
ghz --insecure --proto ./api.proto --call pb.UserService.GetUser -d '{"id":"123"}' -c 50 -z 30s localhost:50051 - 如果非要用 Go 写脚本,必须复用同一个
*grpc.ClientConn,并发控制用信号量,每次调用都包在context.WithTimeout里 - gRPC 压测必须关注 TLS 握手开销和流控行为,
TLSHandshakeTimeout建议设为5 * time.Second~10 * time.Second
没采集 pprof 的压测等于盲测
只看 QPS 和平均延迟,就像开车不看仪表盘。一次靠谱压测 = 三次独立运行 + 每次清缓存 + 每次重启服务 + 每次 GC 两次 + 取中位数。
- 服务端启动时加
pprof:注册net/http/pprof并监听/debug/pprof - 压测中实时采集:
curl http://localhost:6060/debug/pprof/profile?seconds=30 > cpu.pprof,同时抓heap、goroutine、block - 客户端也要调优
http.Client.Transport:设MaxIdleConns和MaxIdleConnsPerHost相同(如 200),IdleConnTimeout设为30 * time.Second - 压测前关掉服务端调试日志:
log.SetOutput(io.Discard),否则 I/O 成瓶颈
最常被忽略的是:三次压测之间必须清页缓存(echo 3 > /proc/sys/vm/drop_caches)、重启服务、手动触发两次 runtime.GC()——否则缓存和 GC 状态污染结果,你其实测的是运气,不是性能。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











