裸写 http.get 压测实为压测本机 ulimit 和 tcp 栈,因默认 transport maxidleconns=0 导致每请求建新连接,引发 time_wait 耗尽端口、dns 毛刺、tls 握手飙升等问题。

直接用 http.Get 写 for 循环压测,不是在测服务,是在测你本机的 ulimit 和 TCP 栈——99% 的“接口扛不住”其实是本地自爆。
为什么不能裸写 http.Get 压测
每次调用 http.Get 都隐式创建新 http.Client,而默认 Transport 的 MaxIdleConns 是 0,等效于每请求建一个全新 TCP 连接:
-
TIME_WAIT连接数暴涨,本地端口几秒耗尽 - 反复 DNS 查询,延迟毛刺明显
- HTTPS 请求频繁 TLS 握手,CPU 和 RT 双飙升
- 并发稍大就报
dial tcp: lookup xxx: no such host或too many open files
这不是 Go 服务的问题,是压测方式本身没隔离资源边界。
k6 里怎么配真实并发而不是“伪并发”
k6 的 vus(虚拟用户)不是线程,而是独立 JS 执行上下文;靠裸写 for + go 不仅无效,还会破坏执行器调度逻辑:
- 测稳定吞吐量:用
constant-vus,例如k6 run --vus 200 --duration 2m script.js - 找系统拐点:用
ramping-vus,比如--stage 30s:50,1m:200,30s:50 - 防脉冲流量:别用
sleep(1),改用per-vu-iterations+exec控制单 VU 行为 - 禁用连接复用(暴露真实瓶颈):加请求头
{ 'Connection': 'close' },HTTP/2 则需http.setHTTP2(false)
看什么指标才反映真实用户体验
http_req_duration 的 mean 值基本没用,它被大量快响应拉低,掩盖长尾问题:
- P95/P99 延迟决定用户是否感知卡顿,必须提取 —— 跑时加
--out json=output.json,再用jq '.metrics.http_req_duration.values.p95' - 脚本内可定义自定义指标配合
check(),例如:check(res, { 'p95 latency duration - 错误率要和延迟联动看:
http_req_failed突增但http_req_durationP99 未涨?大概率是服务主动限流或熔断
容易被忽略的部署细节
本地跑 k6 压测 Golang 接口,最容易翻车的地方不在脚本,而在环境隔离:
- 发压机和被测服务不能共用一台机器,否则 CPU、网络栈、文件描述符全争抢
- Golang 服务要开
pprof(如http.ListenAndServe("localhost:6060", nil)),压测中实时抓goroutine/heapprofile -
k6默认启用 HTTP/2,但 Nginx 等反向代理若未开启h2,会静默降级失败,需确认或显式关掉 - 测试域名务必走真实 DNS,别写
127.0.0.1—— 否则绕过 DNS 缓存和 TLS SNI,测的不是线上链路
真正难的从来不是写脚本,而是让每一次请求都逼近生产流量的真实路径。连接复用、DNS、TLS、代理层……这些中间环节不显式控制,压出来的数字就是幻觉。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











