Go网络错误处理需区分超时与连接失败:用net.Error.Timeout()和errors.Is(err, context.DeadlineExceeded)判超时;用errors.Is(err, syscall.ECONNREFUSED)等判底层错误;按类型差异化重试。

Go语言中处理网络连接错误,关键在于区分超时(timeout)和连接失败(connection refused / no route to host 等),并针对性地重试或终止。标准库的net和net/http包已内置对超时的支持,但需主动识别错误类型,不能仅靠err != nil做统一处理。
使用context.WithTimeout控制连接与读写超时
推荐用context.Context统一管理超时,尤其适用于http.Client或自定义net.Dialer。它能同时覆盖“建立连接耗时过长”和“请求发出后响应迟迟不来”两种场景。
- 对HTTP请求:设置
http.Client.Timeout仅作用于整个请求生命周期(含连接、写入、读取),不够精细;更推荐用http.Client.Transport配合context - 对底层TCP连接:用
&net.Dialer{Timeout: 5 * time.Second, KeepAlive: 30 * time.Second},再通过dialer.DialContext(ctx, "tcp", addr)实现可取消的连接 - 示例:
ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second); defer cancel(),后续所有I/O操作传入该ctx
准确判断是否为超时错误
Go中没有统一的“超时错误类型”,需检查错误是否实现了net.Error接口,并调用.Timeout()方法:
if netErr, ok := err.(net.Error); ok && netErr.Timeout() { /* 是超时 */ }- 注意:
context.DeadlineExceeded也属于超时,但它不是net.Error,需单独判断:errors.Is(err, context.DeadlineExceeded) - 常见误区:直接比较
err.Error()含"timeout"字符串——不可靠,不同系统/驱动返回文案不一致
区分连接拒绝、无路由、DNS失败等底层错误
连接阶段失败通常由net.Dial或http.Transport抛出,错误值多为*net.OpError,其Err字段嵌套具体系统错误:
立即学习“go语言免费学习笔记(深入)”;
-
errors.Is(err, syscall.ECONNREFUSED)→ 对端明确拒绝(服务未启动) -
errors.Is(err, syscall.ENETUNREACH)或errors.Is(err, syscall.EHOSTUNREACH)→ 网络不可达(防火墙、路由问题) -
errors.Is(err, dns.ErrNoAnswer)或strings.Contains(err.Error(), "no such host")→ DNS解析失败(优先用net.DNSError类型断言) - 建议封装一个
classifyNetworkError(err error) NetworkErrType函数,集中处理判断逻辑
重试策略要按错误类型差异化设计
不是所有错误都适合重试。盲目重试可能加剧问题或掩盖真实故障:
- 超时(Timeout / DeadlineExceeded)→ 可重试,建议加退避(如100ms + jitter),最多2~3次
- 连接拒绝(ECONNREFUSED)→ 通常服务宕机,重试意义不大,可快速失败或降级
- DNS失败(No such host)→ 属于配置或基础设施问题,重试无效,应告警而非重试
- I/O错误(read: connection reset by peer)→ 视业务而定,可能是对方异常中断,可尝试重建连接










