应复用 http.Client 实例,全局配置 Transport 连接池与超时参数,并用 context 控制单次请求生命周期,避免新建客户端、默认配置滥用及证书验证误跳过。
复用 http.Client 实例,别每次请求都新建
Go 的 http.Client 是并发安全的,且内部维护连接池(http.Transport),频繁新建会导致大量短连接、TLS 握手开销和文件描述符浪费。常见错误是把 http.Client 写在函数里,每次调用都 new(http.Client)。
正确做法是全局复用一个实例,或按业务场景(如不同超时/代理)定义少数几个客户端:
var defaultClient = &http.Client{
Timeout: 10 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 100,
IdleConnTimeout: 30 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
},
}
-
MaxIdleConns和MaxIdleConnsPerHost需显式设置,否则默认为 2,极易成为瓶颈 - 如果服务端支持 HTTP/2,确保 Go 版本 ≥ 1.6 且未禁用(
ForceAttemptHTTP2: true默认已开启) - 避免在高并发 goroutine 中隐式共享未配置的默认
http.DefaultClient,它用的是包级全局http.DefaultTransport,参数不可控
合理设置 http.Transport 连接池与超时
连接池参数不匹配后端能力,会引发连接堆积、TIME_WAIT 暴增或请求卡死。典型表现是压测时 QPS 上不去、dial tcp: lookup xxx: no such host 突增(DNS 缓存未生效)、或 context deadline exceeded 集中出现。
关键配置项需结合实际链路调整:
-
IdleConnTimeout:空闲连接保活时间,建议设为略小于后端反向代理(如 Nginx)的keepalive_timeout,常见设为 30–60s -
TLSHandshakeTimeout:避免 TLS 握手慢拖垮整个连接池,建议 ≤ 10s -
ResponseHeaderTimeout:从发送完 request 到收到 response header 的上限,防后端卡在逻辑处理但不写 header - 若后端是 Kubernetes Service,注意
MaxIdleConnsPerHost应 ≥ 后端 Pod 数 × 单 Pod 处理并发能力,否则连接会被轮询打散,无法复用
用 context.WithTimeout 控制单次请求生命周期
仅靠 http.Client.Timeout 不足以覆盖所有阻塞点:DNS 解析、TCP 连接建立、TLS 握手、response body 读取等阶段都可能超时。必须用 context 显式控制。