微服务压测需分层隔离、控制变量、复现真实链路;单接口基准测试(go test -bench)仅验证handler逻辑,须禁用日志、mock外部依赖;真实链路压测应使用vegeta或wrk并禁用连接复用;gRPC压测推荐ghz;必须边压边采pprof数据,三次压测取中位数且每次清缓存、重启服务、GC两次。

微服务压测不能只看单个接口吞吐量,必须分层隔离、控制变量、复现真实链路——否则你压的不是服务,是运气。
用 go test -bench 测单个 handler,但别信它能代表线上表现
内置基准测试适合验证 HTTP handler 逻辑是否高效,比如 http.HandlerFunc 内部无锁计算、无阻塞 IO。但它跑在内存网络(httptest.NewRecorder + httptest.NewRequest)上,绕过了 TCP 栈、TLS、反向代理、连接池等真实瓶颈点。
- 务必调用
b.ResetTimer(),否则初始化开销(如路由注册、中间件构造)会污染结果 - 禁用日志:在
Benchmark函数开头加log.SetOutput(io.Discard),否则log.Printf会显著拖慢延迟 - 避免依赖外部状态:数据库、Redis、gRPC 调用必须 mock,否则结果不可控也不可复现
func BenchmarkUserHandler(b *testing.B) {
log.SetOutput(io.Discard)
req := httptest.NewRequest("GET", "/api/user/123", nil)
w := httptest.NewRecorder()
b.ResetTimer()
for i := 0; i < b.N; i++ {
userHandler(w, req) // 纯内存处理
}
}
用 vegeta 或 wrk 压真实 HTTP 接口,但要关掉连接复用
真实链路压测必须走完整网络栈,且禁用客户端连接复用(DisableKeepAlives: true),否则会掩盖服务端连接池、TIME_WAIT、accept 队列溢出等问题。
-
wrk启动命令示例: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/网卡争抢
压 gRPC 微服务?别手写 goroutine,用 ghz 或 grpc-go 自带 benchmark 模式
gRPC 压测难点在于连接管理、流控、Metadata 透传和 TLS 握手开销。手写 goroutine 容易误用 grpc.Dial(每 goroutine 新建连接 → 打爆服务端 fd)、忽略 WithBlock() 导致阻塞、或漏设 WithTimeout 造成统计失真。
立即学习“go语言免费学习笔记(深入)”;
- 推荐
ghz:支持 QPS 控制、并发连接数、自定义 metadata,输出含 P90/P99 延迟和错误码分布 - 若需深度定制(如模拟 token 过期重试),才用 Go client +
sync.WaitGroup+semaphore控制并发,且每个 goroutine 复用同一个*grpc.ClientConn - 服务端必须开启
grpc.StatsHandler并暴露/debug/requests,否则无法区分是网络抖动还是服务内部卡顿
压测中采集 pprof 数据,但别等压完再抓——要边压边采
一次压测不配合 profile,等于没压。CPU、goroutine、block、mutex 四类 profile 必须在压测峰值时实时采集,否则火焰图里全是“平地”,看不出热点。
- 启动服务时加:
import _ "net/http/pprof",并监听:6060(不要和业务端口混用) - 压测中执行:
curl "http://localhost:6060/debug/pprof/profile?seconds=30" > cpu.pprof(注意是 30 秒持续采样,不是快照) - 同时采集 goroutine:
curl "http://localhost:6060/debug/pprof/goroutine?debug=2" > gr.txt,看是否有 goroutine 泄漏 - 关键陷阱:压测前没调
debug.FreeOSMemory()、没清 Redis 缓存、没重启服务——这些都会让第二轮压测数据完全不可比
最常被跳过的一步:三次压测取中位数,且每次之间清空服务端所有缓存、关闭所有连接、GC 两次。否则你以为优化了 20%,其实只是上次压测残留的 page cache 在帮忙。










