go的http.client默认自带连接池,无需手动实现;关键在合理配置maxidleconnsperhost(默认仅2)、idleconntimeout(推荐30–60s)等transport参数,并确保调用resp.body.close()归还连接。

Go 的 http.Client 本身就有连接池,别重复造轮子
直接用 http.DefaultClient 或 new 一个 &http.Client{},底层 http.Transport 默认已启用连接复用和空闲连接池——只要你不显式替换 Transport 或设 DisableKeepAlives = true,连接池就在工作。
常见误判:看到大量 TIME_WAIT、或报错 net/http: request canceled (Client.Timeout exceeded while awaiting headers),往往不是没连池,而是池参数太保守、超时没控好、或 client 被全局滥用。
-
MaxIdleConns控制整个 client 最多缓存多少空闲连接(默认100) -
MaxIdleConnsPerHost才是关键——限制每个 host(如api.pay.example.com)最多几个空闲连接(默认仅2,高并发下极易成瓶颈) -
IdleConnTimeout建议设30s~60s;太短导致刚建好就断,太长可能卡住故障节点
必须显式配置 http.Transport,否则默认超时是 0
http.DefaultClient 的 Timeout 字段只控制整个请求生命周期(Go 1.19+),但底层 TCP 连接、TLS 握手、首字节等待仍可能无限挂起。不设 Transport 级超时,等于裸奔。
-
TLSHandshakeTimeout必须设(如5 * time.Second),否则 TLS 协商失败会卡死 goroutine -
ResponseHeaderTimeout推荐设(如10 * time.Second),防服务端迟迟不发 header -
Proxy别漏——内网环境要用http.ProxyFromEnvironment,否则直连失败 - 若需跳过证书校验(仅测试),加
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}
封装时要隔离 client 实例,别全局共享一个 http.Client
多个业务共用同一个 http.Client(尤其共用 http.DefaultClient),容易互相污染:某个域名 DNS 解析卡住、TLS 握手失败,会拖垮所有后续请求。
- 按业务域或下游服务拆分 client,比如支付调用、短信调用、内部 API 各配独立
http.Client - client 实例应作为依赖注入,避免函数内临时 new,保证可复用、可测、可监控
- 不要在 handler 里反复 new client;也不要让 client 生命周期短于其 Transport 中的连接存活期
重试不能靠 client 自带,得在调用层手动实现
http.Client 不提供重试机制,且 req.Body 是单次读取的——重试时必须新建 *http.Request,否则会 panic。
- 用
context.WithTimeout控制单次请求,不是给 client 设 timeout - 只对幂等方法(
GET/HEAD)或明确可重试状态码(502/503/504)重试 - POST/PUT 默认不重试;若需重试,确保接口幂等,且 body 可安全重建(如从原始 struct 序列化)
- 重试间隔建议指数退避(
100ms → 200ms → 400ms),避免下游雪崩
真正难的不是写个连接池,而是把 Transport 参数、超时链、重试逻辑、client 生命周期这几件事对齐业务特征。比如下游有 20 个子域名,MaxIdleConnsPerHost 设成 50 比设 MaxIdleConns=1000 更有效;又比如调的是不可靠的第三方 API,IdleConnTimeout 设太长反而会让 client 长时间卡在已断开的连接上。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











