压测需精准控制并发:用runtime.gomaxprocs限制os线程数,worker pool+semaphore控goroutine数量,自定义http.transport提升连接复用,pprof定位真实瓶颈,etcd/redis实现分布式协调与状态共识。

用 gomaxprocs 和 runtime.GOMAXPROCS 控制压测并发粒度
Go 压测不是开越多 goroutine 就越快,核心瓶颈常卡在 OS 线程调度和 CPU 核心争抢上。默认 GOMAXPROCS 是 CPU 逻辑核数,但压测时若大量 goroutine 频繁阻塞(比如 HTTP 调用、DNS 解析),实际并行 worker 数远低于预期。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 先用
runtime.NumCPU()查清机器真实可用核数,再根据压测目标调整:runtime.GOMAXPROCS(8)(比如固定为 8,避免自动扩容干扰结果) - 如果压测客户端本身要模拟 10k 并发,别直接起 10k goroutine —— 改用带缓冲的 worker pool,例如用
semaphore.NewWeighted(50)(来自golang.org/x/sync/semaphore)控并发数 - 注意:Docker 容器内
NumCPU()可能返回宿主机核数,需配合cpus限制 + 手动设GOMAXPROCS
http.Transport 连接复用必须显式配置
默认 http.DefaultTransport 的连接池极保守:最多 2 个空闲连接 per host,MaxIdleConnsPerHost 为 2 —— 这在压测中等于主动限流,大量请求排队等连接,net/http: request canceled (Client.Timeout exceeded while awaiting headers) 就是典型表现。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 压测 client 必须自定义
http.Transport,至少设:MaxIdleConns: 1000、MaxIdleConnsPerHost: 1000、IdleConnTimeout: 30 * time.Second - 禁用
ExpectContinueTimeout(默认 1s),避免小请求卡住:ExpectContinueTimeout: 0 - 如果压测目标是 HTTPS,记得调大
TLSHandshakeTimeout,否则高并发下 TLS 握手失败率飙升
用 pprof 抓真实瓶颈,别只看 QPS 数字
集群压测时,QPS 上不去,99% 情况不是代码写得慢,而是系统资源卡在某个隐性环节:DNS 解析阻塞、TCP 连接耗尽、GC 频繁 STW、或 goroutine 泄漏。光看 time.Now() 差值没意义。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 压测前,在服务端加
import _ "net/http/pprof",暴露/debug/pprof/;客户端也建议开pprof.StartCPUProfile() - 重点看:
/debug/pprof/goroutine?debug=2(查泄漏)、/debug/pprof/block(查锁/IO 阻塞)、/debug/pprof/heap(查内存暴涨) - 别信单次
go tool pprof分析 —— 多采样 3–5 次,对比top -cum输出,看是不是总卡在net.(*pollDesc).wait或runtime.mallocgc
分布式协调靠 etcd 或 redis,别手写心跳广播
单机压测跑得再稳,跨节点协同一塌糊涂就白搭。常见错误是用 UDP 广播同步进度、用本地文件记状态、或依赖时间戳对齐 —— 网络延迟、时钟漂移、节点失联会让整个压测数据错乱甚至中断。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用
etcd的 lease + key watch 做 leader 选举和任务分片,每个 worker 注册/loadtest/workers/<code>hostname并续租 - 压测指标汇总统一写入
redis的 hash(如HSET loadtest:summary qps 1240 error_rate 0.02),比轮询拉日志靠谱得多 - 务必设超时清理:etcd lease TTL 不超过 10s,redis key 加
EXPIRE,否则节点宕机后残留状态会污染下次压测
真正难的从来不是起多少并发,而是让所有节点对“当前压测到哪一步”有共识 —— 这点容易被跳过,但一旦出问题,复现和定位成本极高。










