用 docker-compose 搭建轻量、可复现、网络可控的本地高并发测试靶场,需使用 alpine 镜像、固定端口、bridge 网络、healthcheck;压测客户端须禁用 HTTP 连接池,用 hey/vegeta 工具并监控 ss/netstat 连接状态。

用 docker-compose 快速起一个本地高并发测试靶场
不用搭真实服务、不碰 Kubernetes,docker-compose 就够用了。关键是让容器轻量、可复现、网络可控。
常见错误是直接跑一个裸 go run main.go,结果被本机防火墙、DNS 缓存、HTTP/2 连接复用干扰,测出来根本不是服务真实吞吐。
- 用
alpine镜像构建服务二进制,体积小、启动快,避免 glibc 兼容问题 - 暴露端口固定(如
8080),禁用 host 网络,强制走 docker bridge,保证每次请求都走 TCP/IP 栈全路径 - 加
healthcheck,防止压测开始时服务还没 ready 就发请求,导致大量 connection refused
services:
api:
image: myapi:latest
ports: ["8080:8080"]
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 5s
timeout: 3s
retries: 3
Go 压测客户端必须关掉 http.Transport 的连接池
默认的 http.DefaultClient 会复用连接,压测时看起来 QPS 很高,但实际没打穿服务的连接处理逻辑——你测的是连接池性能,不是你的 handler。
真实高频请求场景(比如网关转发、短连接 API)往往连接生命周期极短,必须模拟这个行为。
- 设置
MaxIdleConnsPerHost = 0:彻底禁用空闲连接复用 - 设置
IdleConnTimeout = 1 * time.Second:哪怕有空闲连接,1 秒就丢弃 - 每次请求都新建 TCP 连接,才能暴露 accept 队列溢出、TIME_WAIT 暴增、文件描述符耗尽等问题
示例代码片段:
client := &http.Client{
Transport: &http.Transport{
MaxIdleConnsPerHost: 0,
IdleConnTimeout: 1 * time.Second,
},
}
用 hey 或 vegeta 替代手写 goroutine 循环
自己写 for i := 0; i 看似简单,实则埋雷:goroutine 启动节奏不可控、没有速率限制、无法统计连接级指标(如 connect time、TLS handshake time)。
hey 和 vegeta 是专为 HTTP 压测设计的 CLI 工具,底层用 Go 写,和你的服务栈一致,输出字段对齐明确。
-
hey -n 10000 -c 200 -m POST -d '{"id":1}' http://localhost:8080/api:200 并发打满 10k 请求,输出含 p95/p99 延迟、失败率、req/s -
echo "POST http://localhost:8080/api" | vegeta attack -rate=200 -duration=30s | vegeta report:更细粒度控制 QPS,适合观察尾部延迟随负载变化趋势 - 注意:它们默认走 HTTP/1.1,如需测 HTTP/2,得加
-h2(hey)或确保服务端 TLS 配置正确(vegeta)
监控重点不是 CPU,而是 netstat 和 ss 看连接状态
高频请求下,CPU 高可能只是表象;真正卡点常在内核网络子系统。光看 top 或 pprof 会漏掉关键瓶颈。
在压测同时执行:
-
ss -s:看 total established / time-wait / syn-recv 数量。如果time-wait超过 3w,说明连接释放慢,要调net.ipv4.tcp_fin_timeout或加SO_LINGER -
ss -tn state established '( sport == :8080 )' | wc -l:确认 ESTABLISHED 连接数是否接近你的net.core.somaxconn(Linux 默认 128) -
cat /proc/net/nf_conntrack | wc -l:如果用了 NAT 或 iptables,conntrack 表满会导致 DROP,不是你的 Go 代码问题
这些指标比应用层 pprof 更早暴露系统级压力,尤其当你把服务放进 Docker 后,宿主机和容器 namespace 的网络参数是分开的,容易忽略。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











