应优先用类型断言判断net.Error接口的Timeout()和Temporary()方法;http.Client.Timeout不控制DNS/TLS等前置阶段,需通过http.Transport细化超时;务必关闭resp.Body以防连接泄漏;重试需区分错误类型,避免对不可重试错误盲目重试。

判断 net.Error 类型比直接检查错误字符串更可靠
Go 的网络操作(如 http.Get、net.Dial)返回的错误多数实现了 net.Error 接口,它提供了 Timeout() 和 Temporary() 方法。相比用 strings.Contains(err.Error(), "timeout") 这类脆弱匹配,应优先用类型断言:
if netErr, ok := err.(net.Error); ok {
if netErr.Timeout() {
// 处理超时,比如重试
}
if netErr.Temporary() {
// 可能是临时性故障(如连接被拒、DNS 暂不可用)
}
}注意:Timeout() 返回 true 并不意味着一定是 context.DeadlineExceeded;而 context.DeadlineExceeded 是独立的错误值,需单独判断。
http.Client 的 Timeout 字段不控制 DNS 解析和 TLS 握手
http.Client.Timeout 仅作用于整个请求生命周期(从发送开始到响应 body 读取完成),但 DNS 查询、TLS 握手、TCP 连接建立这些前置阶段不受其约束。常见表现是:设置 Timeout: 5 * time.Second,但遇到 DNS 故障时卡住 30 秒才报错。
- 解决办法是用
http.Transport分别控制各阶段: -
DialContext控制 TCP 连接(含 DNS 解析) -
TLSHandshakeTimeout控制 TLS 握手 -
ResponseHeaderTimeout控制 header 返回时间
示例关键配置:
立即学习“go语言免费学习笔记(深入)”;
client := &http.Client{
Timeout: 5 * time.Second,
Transport: &http.Transport{
DialContext: (&net.Dialer{
Timeout: 3 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
TLSHandshakeTimeout: 3 * time.Second,
ResponseHeaderTimeout: 3 * time.Second,
},
}不要忽略 resp.Body.Close() 导致的连接泄漏和超时误判
即使请求失败(如 resp.StatusCode >= 400),只要 resp 非 nil,resp.Body 就一定存在且必须关闭。否则:
- 底层 TCP 连接不会被复用,连接池快速耗尽
- 后续请求可能因无可用连接而阻塞在
DialContext阶段,表现为“假超时” -
http.DefaultClient默认复用连接,不关 body 会隐式拖慢整个客户端
正确写法始终包含 defer resp.Body.Close(),哪怕只读 status:
resp, err := client.Get("https://api.example.com")
if err != nil {
return err
}
defer resp.Body.Close() // 即使下面要 return error,也必须先 close
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("bad status: %d", resp.StatusCode)
}
重试逻辑里要区分可重试与不可重试错误
不是所有网络错误都适合重试。盲目重试 http.StatusForbidden 或 net.ErrClosed 只会放大问题。
- 建议重试:
net.OpError(连接拒绝、无路由)、context.DeadlineExceeded(非最终超时)、http.ErrUseLastResponse - 禁止重试:
http.StatusUnauthorized、http.StatusForbidden、http.StatusNotFound、net.ErrClosed、url.Error中的 malformed URL - 谨慎重试:TLS 相关错误(如
x509.UnknownAuthorityError)通常不可修复,重试无意义
真正棘手的是中间状态——比如 TCP 连接成功但服务端进程崩溃,此时收到 RST 包,Go 报 read: connection reset by peer,这类错误是否重试,得结合业务语义判断。










