不能直接用http.get压测,因其每次新建未调优client导致dns不复用、连接爆炸、tls堆积及too many open files;必须配置maxidleconns与maxidleconnsperhost相等、idleconntimeout和tlshandshaketimeout四参数,并用time.ticker控频。

直接用 http.Get 或裸起 goroutine 做压测,结果基本不可信——QPS 虚低、错误频发、本机先崩,不是服务扛不住,是你客户端配置全错了。
为什么不能直接用 http.Get 发起压测请求
每次调用 http.Get 都会新建一个未调优的 http.Client,其底层 Transport 的 MaxIdleConns 默认为 0,等于彻底禁用连接池。后果很直接:
-
dial tcp: lookup xxx: no such host:DNS 缓存不复用,每次请求都重新解析 -
too many open files:每个请求建新 TCP 连接,ulimit -n很快耗尽 - HTTPS 请求 TLS 握手堆积,CPU 占满、RT 毛刺剧烈
- 大量
TIME_WAIT连接堆积,本机端口快速枯竭
必须显式配置 http.Transport 四个关键参数
这四个值不是“建议配”,而是压测能跑稳的硬性前提,缺一不可,且有强依赖关系:
-
MaxIdleConns和MaxIdleConnsPerHost必须设为相同值(例如都设200),否则后者会被前者截断失效 -
IdleConnTimeout建议30 * time.Second:太短导致频繁重建连接,太长则空闲连接占资源 -
TLSHandshakeTimeout建议5 * time.Second~10 * time.Second:避免个别慢握手阻塞整个 worker goroutine - 若需模拟“首次访问”行为(比如测真实用户冷启动),则应反向设为禁用复用:
MaxIdleConns = 0、MaxIdleConnsPerHost = 0、IdleConnTimeout = 0、ForceAttemptHTTP2 = false
示例片段:
client := &http.Client{
Transport: &http.Transport{
MaxIdleConns: 200,
MaxIdleConnsPerHost: 200,
IdleConnTimeout: 30 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
},
}
用 time.Ticker + sync.WaitGroup 控制稳定 QPS 节奏
裸写 for i := 0; i 不是“100 QPS”,是“100 并发瞬发”,测的是你本机网络栈极限,不是服务吞吐能力。
- 用
time.Ticker替代time.Sleep:精度更高,且能自动补偿单次请求耗时波动 - 推荐“生产者-消费者”模型:一个 ticker goroutine 定期往
chan struct{}写信号,N 个 worker 从 channel 读取并执行请求 -
chan struct{}零内存开销,比传 request 对象更轻量 - 务必配合
sync.WaitGroup等待所有请求完成,避免主 goroutine 提前退出
别忽略系统层限制和压测目标对齐
再好的 Go 代码,卡在系统层就全白搭:
- Linux 下必须同步调大文件描述符限制:
ulimit -n 65536,否则压到一半必然卡在socket: too many open files - 若压测目标是 HTTPS 接口,测试环境可加
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}省去证书校验开销(仅限测试) - 压测机与被测服务必须物理或网络隔离,避免互相干扰
- 真实接口压测需明确目标:是测“连接复用下的最大吞吐”,还是“冷启动下的首屏延迟”?两者配置完全相反
最常被跳过的一步:没确认 runtime.GOMAXPROCS 是否合理。容器中 runtime.NumCPU() 可能返回宿主机核数,得结合 cpus 限制手动设死,否则并发调度失真。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











