用 net/http 并发请求时须自定义 http.Client:设 Timeout(如10s)、MaxIdleConns 与 MaxIdleConnsPerHost(建议≥2000)、调整 IdleConnTimeout;并发控制用 sync.WaitGroup + channel,避免默认配置导致客户端瓶颈。

用 net/http 发起并发请求,别碰 http.DefaultClient 默认超时
Go 自带的 http.Client 能扛住高并发,但默认配置会拖垮压测结果:http.DefaultClient 的 Timeout 是 0(无限等待),而底层 Transport 的 MaxIdleConns 和 MaxIdleConnsPerHost 默认只有 100,一压就卡在连接池排队。实际压测必须显式构造 client:
- 设置
Timeout控制单次请求上限,比如10 * time.Second - 调大
Transport.MaxIdleConns和Transport.MaxIdleConnsPerHost,建议至少设为 2000 - 关闭
Transport.IdleConnTimeout或设为较大值,避免连接被过早回收
不改这些,你看到的 QPS 上不去,不是服务端瓶颈,是客户端自己堵死了。
控制并发数用 sync.WaitGroup + channel,别用 for 循环直接起 goroutine
写个 for i := 0; i 看似简单,实则危险:瞬间拉起上万个 goroutine,内存暴涨、调度开销大,还可能触发系统级限制(如文件描述符耗尽)。正确做法是用固定 worker 池控流:
- 用
make(chan struct{}, N)做信号通道,N 即最大并发数(如 200) - 每个请求前
sem ,结束后 <code><-sem - 配合
sync.WaitGroup等待全部完成,别依赖time.Sleep
漏掉 channel 泄露或 wg.Add/Wait 不配对,会导致程序提前退出或永远 hang 住。
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
统计响应时间得用 time.Now() 和 time.Since(),别依赖日志打点或服务端返回
压测关注的是「客户端视角的真实耗时」,包括 DNS 解析、TCP 握手、TLS 协商、发送、等待、接收全过程。服务端日志里的耗时只反映处理阶段,且受时钟不同步影响。实操必须在 client 侧精确采集:
- 在
http.Do()前调用start := time.Now() - 请求返回后立刻算
latency := time.Since(start) - 把所有
latency存进 slice 或流式计算 p95/p99,别等压完再从日志 grep
忽略重定向、忽略错误请求(如 status != 200)都得单独判断,否则统计失真——比如超时请求被记成 0ms,p99 就完全没意义。
解析 JSON 响应失败时,json.Unmarshal 报 invalid character 很可能是服务端返回了 HTML 错误页
压测中常遇到 json: cannot unmarshal string into Go value of type map[string]interface{} 或更模糊的 invalid character,这不是代码 bug,而是目标服务在高压下返回了 502/503 页面(比如 Nginx 默认错误页),内容是 HTML,却仍带 Content-Type: application/json。排查步骤:
- 先打印原始
resp.Body前 200 字节,看是不是<!DOCTYPE html> - 检查
resp.StatusCode,非 2xx/3xx 响应一律跳过 JSON 解析 - 加一层
strings.HasPrefix(string(body), "<")防御性判断
这个坑特别隐蔽:本地跑得通,一上压测环境就崩,因为只有高并发才触发网关降级。不验证响应体类型,解析逻辑会静默失败,指标全乱。


















