别用 http.DefaultClient,它在生产环境大概率会出问题——连接不复用、无超时、卡死请求、无法注入日志或追踪头;因其底层 http.Transport 的 MaxIdleConns 和 MaxIdleConnsPerHost 默认为 0、IdleConnTimeout 和 TLSHandshakeTimeout 默认为 0,导致连接永不复用、空闲连接傻等、TLS 握手无限卡住,且 Timeout 字段不控底层建连与首字节等待。

直接说结论:别用 http.DefaultClient,它在生产环境大概率会出问题——连接不复用、无超时、卡死请求、无法注入日志或追踪头。
为什么不能直接用 http.DefaultClient
它底层的 http.Transport 几乎全是零值配置:
-
MaxIdleConns和MaxIdleConnsPerHost默认为 0 → HTTP/1.1 连接永不复用,短连接风暴下 fd 耗尽 -
IdleConnTimeout默认 0 → 长连接被服务端断开后,client 还在傻等,goroutine 挂起 -
TLSHandshakeTimeout默认 0 → TLS 握手失败时无限等待(尤其在内网或弱网络下) -
Timeout字段(Go 1.19+ 才作用于整个请求)不控制底层 TCP 建连、TLS 握手、首字节等待 → 单个请求仍可能卡住数分钟
http.Transport 必须显式配置的 4 个参数
自己 new 一个 http.Client 时,Transport 至少要设这四项:
-
MaxIdleConns: 100—— 全局最大空闲连接数 -
MaxIdleConnsPerHost: 100—— 每个 host 最大空闲连接数(否则默认 2,极易打满) -
IdleConnTimeout: 30 * time.Second—— 空闲连接存活时间,避免被服务端静默断开后 client 不知情 -
TLSHandshakeTimeout: 10 * time.Second—— 强制 TLS 握手超时,防止握手卡死
代理和证书校验按需加:Proxy: http.ProxyFromEnvironment(支持 HTTP_PROXY 环境变量),测试时才加 TLSClientConfig: &tls.Config{InsecureSkipVerify: true}。
立即学习“go语言免费学习笔记(深入)”;
超时必须用 context,不是 http.Client.Timeout
http.Client.Timeout 只覆盖 Go 1.19+ 的整个请求生命周期,但对以下环节无效:
- TCP 连接建立(DNS 解析 + SYN 握手)
- TLS 握手(已由
TLSHandshakeTimeout单独控) - 等待响应首字节(即服务端处理耗时)
正确做法是每次调用都传 context.WithTimeout(ctx, 5*time.Second),并在 http.Request.WithContext() 中注入。重试逻辑也必须基于 context —— 每次重试新建 *http.Request,因为 req.Body 是单次读取的,重用会 panic。
封装时最容易漏掉的三个点
很多封装只做了方法包装,却忽略这些实际踩坑点:
- 没统一设置
User-Agent或Accept头 → 部分 API 拒绝无 UA 请求 - 没做幂等性判断就盲目重试 → 对
POST默认重试可能造成重复下单 - 没把
req.Header.Set("X-Request-ID", ...)和链路追踪 ID 绑定 → 故障排查时无法串联日志
真正可用的封装,核心不在“支持 GET/POST”,而在 transport 控制力、context 传递完整性、以及错误分类(比如区分 net.OpError 和 url.Error)是否清晰。这些细节不处理,上线后就是半夜告警。


















