http.defaultclient压测易崩因默认连接池小且未设超时,应自定义client调大maxidleconns等参数并设idleconntimeout;wg使用需规范;优先用runtime.nanotime()计时;注意ulimit和系统瓶颈。

为什么 http.DefaultClient 在压测里会拖慢甚至崩掉
Go 默认的 HTTP 客户端用的是共享的连接池,MaxIdleConns 和 MaxIdleConnsPerHost 默认都是 100,压测时并发一高,连接复用跟不上,大量新建连接 + TIME_WAIT 堆积,很快卡住或报 net/http: request canceled (Client.Timeout exceeded while awaiting headers)。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 自己构造
http.Client,显式配置Transport -
MaxIdleConns和MaxIdleConnsPerHost至少设为压测并发数的 2–3 倍(比如压 500 并发,设 1500) - 务必设置
IdleConnTimeout(如 30s),否则空闲连接长期占着不放,服务端扛不住 - 禁用
KeepAlive?别乱关——压测反而更需要复用,关了只会加重握手开销
tr := &http.Transport{
MaxIdleConns: 1500,
MaxIdleConnsPerHost: 1500,
IdleConnTimeout: 30 * time.Second,
}
client := &http.Client{Transport: tr}用 sync.WaitGroup 控制并发但没等完就退出了
常见错误:启一堆 goroutine 发请求,wg.Add(1) 放在循环外、或漏了 defer wg.Done(),导致 wg.Wait() 提前返回,结果统计为空、QPS 算成 0。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
-
wg.Add(n)必须在启动 goroutine 前一次性加完,不要在循环里边启边加 - 每个 goroutine 内第一行写
defer wg.Done(),别写成wg.Done()放在最后(panic 时会漏调) - 压测时间短(比如 10 秒)时,别只靠
time.Sleep等——要用context.WithTimeout包裹整个压测逻辑,避免 goroutine 漏收尾
time.Now() 和 time.Since() 在高频压测里不准?
不是不准,是开销大。每发一次请求都调两次系统时钟(开始+结束),万级 QPS 下这部分耗时能占到总耗时 5%+,尤其在容器或虚拟机里更明显。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 改用
runtime.nanotime()获取纳秒级单调时钟(无系统调用,不随系统时间跳变) - 记录起始用一次
start := runtime.nanotime(),每次响应后elapsed := runtime.nanotime() - start - 注意单位转换:
time.Duration(elapsed)是纳秒,除以1e6得毫秒,别手误除成1e9导致全显示 0ms
并发数设太高,Linux 报 too many open files
每个 HTTP 连接至少占一个文件描述符(fd),Go 默认不限制 goroutine 数,但 OS 限制 fd 总数(ulimit -n 通常 1024)。500 并发 × 每个连接平均 2–3 个 fd(TCP socket + TLS handshake 等),很容易超限。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 压测前先查
ulimit -n,临时调高:ulimit -n 65536 - 代码里加资源检查:用
syscall.Getrlimit(syscall.RLIMIT_NOFILE, &r)读当前限制,比目标并发 × 3 小就 warn - 别盲目堆并发——QPS 不一定随并发线性涨,拐点常出现在 200–800 之间,看
http.Server的Handler是否有锁、DB 连接池是否够用
真正卡住的地方往往不在 Go 代码里,而在你没配的数据库连接池、Nginx upstream 限流、或者服务端那台机器的 net.ipv4.ip_local_port_range 范围太小——压测工具只是照出问题的镜子,不是问题本身。











