Go压测需先定位瓶颈:用go test -bench测函数级性能但不反映真实HTTP吞吐,应结合wrk压测与pprof采样分析GC、内存分配及系统限制。

Go 程序压测不能只靠 ab 或 wrk 打 HTTP 接口就完事——你得先确认瓶颈在哪儿,否则压出来的数字毫无意义。
用 go test -bench 做基准测试,但别当真
它适合测单个函数或小逻辑的 CPU/内存开销,比如 json.Marshal 和 encoding/json vs easyjson 的差异。但它不模拟真实并发、不走网络栈、不触发 GC 压力,更不反映 HTTP 服务整体吞吐。
- 必须加
-benchmem看分配次数和字节数,BenchmarkFoo-8 1000000 1245 ns/op 128 B/op 4 allocs/op中的4 allocs/op比耗时更值得警惕 - 避免在
Benchmark函数里写time.Sleep或依赖外部状态(如文件、数据库),否则结果不可复现 - 用
benchstat(go install golang.org/x/perf/cmd/benchstat@latest)比对前后版本差异,看Δ是否显著
HTTP 服务压测:选 wrk 而非 ab,并配 pprof 实时采样
ab 是单线程发请求,连接复用弱,容易把自己打满,根本压不出 Go HTTP server 的真实能力;wrk 支持多线程 + 连接池,更贴近生产流量模型。
- 启动服务时务必开启 pprof:
import _ "net/http/pprof"并起一个独立监听端口(如http.ListenAndServe("localhost:6060", nil)) - 压测中实时抓 profile:
wrk -t4 -c100 -d30s http://localhost:8080/api/users同时另开终端执行curl "http://localhost:6060/debug/pprof/profile?seconds=30" > cpu.pprof - 分析时优先看
go tool pprof cpu.pprof,输入top10,重点关注runtime.mallocgc、net/http.(*conn).serve、io.copyBuffer占比
识别真实瓶颈:GC 频率比 QPS 更关键
很多服务压到 5000 QPS 就卡顿,一查 runtime.ReadMemStats 发现每秒 GC 3–5 次,每次停顿 2–5ms——这不是并发不够,是对象生命周期太短或缓存没复用。
立即学习“go语言免费学习笔记(深入)”;
- 在 HTTP handler 中避免频繁创建切片(如
make([]byte, 0, 1024)比make([]byte, 1024)更好)、避免字符串拼接转bytes.Buffer - 用
sync.Pool缓存高频分配对象(如 JSON 解码器、buffer、结构体实例),但注意 Pool 中对象可能被 GC 回收,不能存带状态或需清理的资源 - 通过
go tool pprof -http=:8081 cpu.pprof查看火焰图,如果runtime.gcBgMarkWorker占比高,说明堆增长太快,该检查log.Printf、fmt.Sprintf、未关闭的http.Response.Body了
别忽略系统层限制:文件描述符、TIME_WAIT、内核参数
Go 程序能轻松启上万 goroutine,但 Linux 默认 ulimit -n 是 1024,netstat -ant | grep TIME_WAIT | wc -l 超过 28000 就可能丢包——这时调优代码毫无意义。
- 压测机和服务机都要改:
echo "* soft nofile 65536" >> /etc/security/limits.conf,然后重新登录生效 - 服务端加
Server.ReadTimeout和WriteTimeout,避免慢连接长期占着 fd;客户端用http.Transport设置MaxIdleConnsPerHost(建议设为 100+)和IdleConnTimeout(如 30s) - 内核级调优(仅限服务机):
net.ipv4.tcp_tw_reuse = 1、net.core.somaxconn = 65535,否则压测中大量connect: cannot assign requested address
压测最难的不是跑出高数字,而是让 go tool pprof 显示的每一帧都可解释、可推导、可验证——如果你看不出为什么 runtime.mapassign 占了 37% CPU,那就还没真正开始分析。











