go test -bench无法测出真实qps,因其仅测试单进程goroutine调度效率,不走真实tcp连接与网络栈;真实qps必须通过hey、wrk或vegeta等外部工具,发起多连接并统计requests/sec。

Go 的 go test -bench 无法测出函数在高并发下的真实 QPS,它只反映单进程内 goroutine 调度效率,不产生真实 TCP 连接、不压网络栈、不触发连接池耗尽或 TLS 握手瓶颈。QPS 是接口级指标,必须走真实 HTTP 请求链路才能获得。
go test -bench 测不出 QPS 的根本原因
testing.B 默认串行执行;即使调用 b.RunParallel,也只是在单个测试进程内调度 goroutine,所有请求共享同一个 http.Client、复用连接、绕过 socket 创建/关闭开销。结果是:数值虚高、延迟失真、完全看不出 net/http.(*Transport).getConn 阻塞、runtime.futex 等真实瓶颈。
- 你看到的
ns/op是“一次 HTTP 调用逻辑耗时”,不是“每秒能处理多少次请求” - QPS = 并发数 × (1 / 平均延迟),但
go test给不出可信赖的平均延迟(尤其当服务端有超时、重试、连接复用时) - 默认
http.DefaultClient.Transport.MaxIdleConnsPerHost = 2,100 并发下大量请求排队等连接,go test却不暴露这个排队时间
测 QPS 必须用外部压测工具
真实 QPS 只能在客户端发起多 TCP 连接、走完整网络协议栈后统计。推荐三类工具,按场景选:
-
hey:轻量,适合快速验证,支持-c(并发数)、-n(总请求数)、-m POST、-H "Connection: keep-alive" -
wrk:高吞吐,Lua 脚本可定制逻辑(如带 token 鉴权),默认启用 keep-alive,更贴近生产流量 -
vegeta:支持持续压测(-rate=100 -duration=60s)、阶梯增压、导出 JSON 结果,适合做稳定性回归
例如:hey -c 50 -n 5000 -H "Authorization: Bearer xxx" http://localhost:8080/api 输出里直接有 Requests/sec,这才是你要的 QPS。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
Go 客户端压测脚本要显式调大连接池
如果你自己写 Go 脚本调 http.Client 压测(比如用 vegeta 自定义 generator),不调连接池参数会严重低估服务吞吐:
- 默认
http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = 2,50 并发下 48 个请求在排队 - 必须显式设置:
tr := &http.Transport{MaxIdleConnsPerHost: 1000},再用&http.Client{Transport: tr} - 别复用
http.DefaultClient——它是全局变量,多个测试用例间会互相污染 - 如果压测 HTTPS 接口,还要禁用证书校验(仅限本地):
tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
QPS 低时优先排查这三处而非 Go 代码
本地 hey -c 100 -n 10000 QPS 卡在 300 左右,90% 情况和你的 Go handler 逻辑无关:
- 服务端
http.Server.ReadTimeout或WriteTimeout设太小(如 5s),导致连接频繁中断重连 - 客户端没开 keep-alive:
hey默认 HTTP/1.1 但不加 header,换wrk -H "Connection: keep-alive"或加-H "Connection: keep-alive" - Linux 本地端口耗尽:
net.ipv4.ip_local_port_range默认32768 60999(约 28K 端口),100 并发 × 多轮请求极易撞上限;临时扩为1024 65535
真正该怀疑 Go 代码性能时,先用 curl http://localhost:6060/debug/pprof/profile?seconds=30 抓 CPU profile,重点看 runtime.futex 和 sync.runtime_SemacquireMutex 占比——高了才是锁瓶颈,而不是一上来就改算法。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










