推荐用 hey(而非 ab)压测本地 go web 服务,需关闭调试日志、配置 http.transport 限流、使用 context 控制超时,并通过 pprof 监控 goroutine、gc 和阻塞点;gomaxprocs 通常无需手动调整。

用 ab 或 hey 快速压测本地 Go Web 服务
Go 写的 HTTP 服务启动后,别急着上 curl 手动点,先确认它扛不扛得住并发。推荐直接用命令行工具:Linux/macOS 下 ab(Apache Bench)够用,但不支持 HTTP/2;更推荐 hey(Go 写的,支持 HTTP/2、连接复用、更贴近真实客户端行为)。
常见错误是压测前没关掉 Go 的调试日志(比如 log.Println),导致 I/O 成瓶颈,压出来的 QPS 偏低,误判服务性能。
-
hey -n 1000 -c 50 http://localhost:8080/api/users:发 1000 次请求,最大并发 50 - 加
-m POST -d '{"id":1}' -H "Content-Type: application/json"测试带 body 的接口 - 加
-h2强制走 HTTP/2(前提是你的 Go 服务启用了 TLS 或明确配置了 HTTP/2 支持)
在 Go 代码里用 http.DefaultClient 模拟并发请求做集成测试
命令行工具适合粗筛,但你要验证“某个中间件是否真生效”“熔断逻辑是否触发”,就得在 Go 里写可控的并发请求逻辑。别自己手撸 goroutine + sync.WaitGroup,容易漏关连接、堆内存暴涨。
关键点在于复用连接和控制超时:http.DefaultClient 默认复用连接,但它的 Transport 没设限,高并发下可能耗尽文件描述符或卡死在 DNS 解析。
立即学习“go语言免费学习笔记(深入)”;
- 显式配置
http.Transport:设置MaxIdleConns、MaxIdleConnsPerHost(建议都设为 100)、IdleConnTimeout(如 30 * time.Second) - 每个请求必须带
context.WithTimeout,否则失败请求会无限 hang 住 goroutine - 避免用
time.Sleep控制节奏,改用time.Ticker或带缓冲 channel 限流
监控 Go Web 服务的实时指标:别只看 CPU 和内存
压测时盯着 top 看 CPU 是最低效的方式。Go 自带 expvar 和 net/http/pprof,能暴露真正影响吞吐的关键信号:GC 频率、goroutine 数量、HTTP handler 耗时分布、阻塞的系统调用。
容易被忽略的是:pprof 默认只监听 localhost,部署到服务器后如果没改 http.ListenAndServe("localhost:6060", nil) 为 ":6060",外部就访问不到;另外 /debug/pprof/goroutine?debug=2 输出的是完整栈,别在高并发时频繁刷这个,会拖慢服务。
- 启动时加
go func() { http.ListenAndServe(":6060", nil) }()暴露 pprof - 用
go tool pprof http://localhost:6060/debug/pprof/heap看内存分配热点 - 用
go tool pprof http://localhost:6060/debug/pprof/block查 goroutine 卡在哪(比如锁竞争、channel 阻塞)
为什么 runtime.GOMAXPROCS 在压测中经常被误调
很多人一压测发现 QPS 上不去,第一反应是调大 GOMAXPROCS。但现代 Go(1.21+)默认已设为系统逻辑 CPU 数,手动调高反而可能因调度开销增加、缓存失效变多而降低性能。
真正该调的是 OS 层面的东西:文件描述符限制(ulimit -n 65536)、TCP 连接队列(net.core.somaxconn)、以及 Go 服务自身的 http.Server.ReadTimeout / WriteTimeout —— 这些不设好,GOMAXPROCS 再大也卡在 I/O 等待上。
- 查当前 goroutine 数:
curl http://localhost:6060/debug/pprof/goroutine?debug=1 | grep -c "goroutine" - 查 GC 暂停时间:
go tool pprof http://localhost:6060/debug/pprof/gc - 压测中如果
runtime.NumGoroutine()持续上涨不降,大概率是 handler 里漏了defer resp.Body.Close()或没处理超时
Go 的并发模型很干净,但压测不是比谁开的 goroutine 多,而是看请求从进来到出去的每一步有没有隐性阻塞 —— 这些地方藏得深,得靠 pprof 和 client 侧的精细控制才能揪出来。










