go如何准确识别各种网络错误
本文带你搞懂懂Go的网络错误本质——不同网络异常(超时、DNS解析失败、连接被拒)对应不同错误类型,以及总结的Go网络错误识别方法
最常见的网络错误:
-
无法解析host ip
-
TCP无法建立连接
-
读写超时
-
……
一、基础:字符串匹配
很多人识别网络错误时,会写这种“反模式”代码:
package main
import (
"fmt"
"net/http"
)
func main() {
resp, err := http.Get("https://example.com")
if err != nil {
// 错误做法:靠字符串包含判断错误类型
if strings.Contains(err.Error(), "timeout") {
fmt.Println("超时错误")
} else if strings.Contains(err.Error(), "refused") {
fmt.Println("连接被拒")
} else {
fmt.Println("未知错误")
}
return
}
defer resp.Body.Close()
}这种根据错误信息进行字符串匹配进行判断的方法有非常明显的局限性:该错误信息依赖于操作系统,不同的操作系统对于同一错误返回的字符串信息可能是不同的。因此,这种判断网络错误类型的方法是不可靠的
二、进阶:接口类型断言
go error的接口类型
type error interface {
Error() string
}go的网络标准库返回的net.Error的接口类型
type Error interface {
error
Timeout() bool // Is the error a timeout?
Temporary() bool // Is the error temporary?
}net.Error接口类型的具体实现net.OpError
type OpError struct {
// Op is the operation which caused the error, such as
// "dial", "read" or "write".
Op string
// Net is the network type on which this error occurred,
// such as "tcp" or "udp6".
Net string
// For operations involving a remote network connection, like
// Dial, Read, or Write, Source is the corresponding local
// network address.
Source Addr
// Addr is the network address for which this error occurred.
// For local operations, like Listen or SetDeadline, Addr is
// the address of the local endpoint being manipulated.
// For operations involving a remote network connection, like
// Dial, Read, or Write, Addr is the remote address of that
// connection.
Addr Addr
// Err is the error that occurred during the operation.
Err error
}其中,net.OpError.Err 可能是以下几种类型:
- net.DNSError
- net.InvalidAddrError
- net.UnknownNetworkError
- net.AddrError
- net.DNSConfigError
- *os.SyscallError
因此可用断言式进行判断
func isCaredNetError(err error) bool {
netErr, ok := err.(net.Error)
if !ok {
return false
}
if netErr.Timeout() {
log.Println("timeout")
return true
}
opErr, ok := netErr.(*net.OpError)
if !ok {
return false
}
switch t := opErr.Err.(type) {
case *net.DNSError:
log.Printf("net.DNSError:%+v", t)
return true
case *os.SyscallError:
log.Printf("os.SyscallError:%+v", t)
if errno, ok := t.Err.(syscall.Errno); ok {
switch errno {
case syscall.ECONNREFUSED:
log.Println("connect refused")
return true
case syscall.ETIMEDOUT:
log.Println("timeout")
return true
}
}
}
return false
}这种错误判定方式除了能解决最开始提到的可靠性和准确性问题,也具有良好的普适性。即基于 net 的其他标准库,如 net/http 也支持这种错误判断方式。
常见问题
Q1:为什么用errors.Is判断net.ErrTimeout会失效?
因为很多网络库会包装超时错误(如HTTP客户端的超时错误是*http.httpError,而非直接返回net.ErrTimeout)。解决方法:先用errors.As断言为net.Error,再调用Timeout()方法判断,如:
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
// 是超时错误
}Q2:Windows和Linux的网络错误类型有差异吗?需要单独适配吗?
核心错误类型(如net.Error、net.DNSError)是跨平台一致的,但底层错误描述可能不同。
解决方案:只靠类型断言和官方方法判断,绝对不依赖错误字符串。
比如判断“连接被拒”,用errors.Is(opErr.Err, net.ErrConnectionRefused),而非匹配“connection refused”字符串。
Q3:HTTP状态码408(请求超时)和503(服务不可用)算网络错误吗?怎么识别?
不算“底层网络错误”,而是“应用层错误”,需通过响应状态码判断。示例代码:
resp, err := client.Get(url)
if err != nil {
// 处理底层网络错误(如超时、连接被拒)
} else {
defer resp.Body.Close()
switch resp.StatusCode {
case 408:
fmt.Println("应用层超时:请求超时")
case 503:
fmt.Println("应用层错误:服务不可用(可重试)")
case 200:
fmt.Println("请求成功")
default:
fmt.Println("其他应用层错误:", resp.StatusCode)
}
}总结
Go网络错误识别的核心不是“记住所有错误类型”,而是掌握3个黄金法则,就能应对99%的场景:
- 优先用类型断言:先通过
errors.As断言为net.Error等标准类型,再调用官方方法(如Timeout())判断,拒绝字符串匹配; - 区分层级错误:底层网络错误(超时、连接被拒)用
net包类型判断,应用层错误(HTTP状态码408/503)用响应信息判断; - 重试需谨慎:只有“临时错误”(如超时、网络抖动)可重试,“永久性错误”(如域名不存在、连接被拒)绝对不重试,避免触发限流。
最后,网络错误处理看似细节,实则直接影响服务可用性——精准识别是前提,合理重试是关键。
如果大家有GRPC、WebSocket等特殊场景的错误识别问题,欢迎在评论区交流!
版权声明
未经授权,禁止转载本文章。
如需转载请保留原文链接并注明出处。即视为默认获得授权。
未保留原文链接未注明出处或删除链接将视为侵权,必追究法律责任!
本文原文链接: https://fiveyoboy.com/articles/go-http-err-code/
备用原文链接: https://blog.fiveyoboy.com/articles/go-http-err-code/