Go中需用net.Error接口区分超时(Timeout()为true)和临时连接失败(Temporary()为true但Timeout()为false),避免字符串匹配;应配置带各阶段超时的HTTP Client并精准判断错误类型。

Go 语言中做网络请求时,区分超时(timeout)和连接失败(如 DNS 解析失败、拒绝连接、无路由等)非常关键——因为它们代表不同层级的问题,应采取不同的重试或降级策略。核心在于正确识别错误类型,而不是用 err != nil 一概而论。
使用 net.Error 判断超时和临时性错误
Go 的标准库(如 net/http)在底层会返回实现了 net.Error 接口的错误。该接口有两个重要方法:
-
Timeout() bool:是否为超时错误(包括连接超时、读写超时) -
Temporary() bool:是否为临时性错误(如连接被拒绝、临时 DNS 失败),可能稍后恢复
注意:Timeout() 为 true 时,Temporary() 通常也为 true;但反过来不成立。例如,DNS 解析超时是 timeout,而 TCP 连接被拒(connection refused)是 temporary 但不是 timeout。
构造带超时的 HTTP Client 避免阻塞
不要依赖 http.DefaultClient,它没有默认超时,容易卡死。应显式配置 http.Client:
立即学习“go语言免费学习笔记(深入)”;
client := &http.Client{
Timeout: 10 * time.Second, // 整个请求生命周期上限(含连接、写、读)
Transport: &http.Transport{
DialContext: (&net.Dialer{
Timeout: 5 * time.Second, // 建连超时
KeepAlive: 30 * time.Second,
}).DialContext,
TLSHandshakeTimeout: 5 * time.Second, // TLS 握手超时
ExpectContinueTimeout: 1 * time.Second,
},
}这样,你就能在不同阶段捕获对应错误:
- 建连超时 →
net.OpError,Timeout()==true - TLS 握手超时 → 同样是
Timeout()==true - 服务端关闭连接或网络中断 →
Temporary()==true,Timeout()==false - DNS 解析失败(如域名不存在)→
Temporary()==true(Go 1.19+ 默认为 true) - 目标地址不可达(如防火墙拦截、路由失败)→ 通常是
Temporary()==true
精准判断错误类型的推荐写法
避免字符串匹配(如 strings.Contains(err.Error(), "timeout")),应使用类型断言和接口判断:
resp, err := client.Do(req)
if err != nil {
var netErr net.Error
if errors.As(err, &netErr) {
if netErr.Timeout() {
log.Println("请求超时,可能是网络延迟或服务响应慢")
// 可考虑重试(需结合幂等性)
} else if netErr.Temporary() {
log.Println("临时性网络错误,如连接被拒、DNS 失败")
// 可立即重试(例如换 endpoint 或稍后重试)
} else {
log.Println("非临时、非超时网络错误,如地址格式错误")
// 一般是代码/配置问题,不应重试
}
} else {
log.Printf("非网络类错误:%v", err)
// 如 JSON 解析失败、空响应体等
}
return
}补充提示:某些错误(如 http.ErrUseLastResponse 或重定向循环)不属于 net.Error,需单独处理;HTTP 状态码(如 404、503)属于业务层错误,应在 resp.StatusCode 中检查,而非 err。
连接失败的典型错误示例与含义
常见非超时但连接失败的情况及判断方式:
-
dial tcp 10.0.0.1:80: connect: no route to host→Temporary()==true,系统找不到通往目标的路由 -
dial tcp: lookup example.invalid: no such host→Temporary()==true(Go ≥1.19),DNS 查询失败 -
dial tcp 127.0.0.1:8080: connect: connection refused→Temporary()==true,目标端口无服务监听 -
read tcp 192.168.1.100:54321->192.168.1.200:80: read: connection reset by peer→Temporary()==false,对端异常关闭,一般不重试
这些错误多数可通过 errors.As(err, &netErr) 捕获并分类,无需解析错误字符串。










