Go 的 net.Error 必须实现 Temporary() 方法,因为标准库(如 net、http)依赖该方法返回 bool 来区分临时错误(可重试)与致命错误;不实现则上层无法识别重试时机。

Go 的 net.Error 为什么必须实现 Temporary() 方法
临时错误不是靠字符串匹配或自定义字段判断的,而是 Go 标准库(尤其是 net、http)约定用 Temporary() 方法返回 bool 来明确分类。不实现它,上层代码(比如 http.Client 的重试逻辑)就完全无法识别“这个连接失败能不能再试一次”。
-
Temporary()是net.Error接口的一部分,只要类型实现了它,就能被标准库自动识别为“可能恢复的错误” - 典型场景:DNS 解析超时、TCP 连接拒绝(
connection refused)、读写超时 —— 这些都该返回true - 致命错误比如证书验证失败、协议解析错误、
invalid memory addresspanic 导致的error值 —— 这些不该实现net.Error,更不该让Temporary()返回true
自己封装错误时怎么正确实现 Temporary()
别直接在 struct 里加个 Temporary bool 字段然后写个方法返回它 —— 这样虽然能编译,但容易误传、难维护,且和标准库语义脱节。
- 推荐方式:嵌入
net.OpError或组合&net.OpError{...},它已完整实现Temporary()逻辑(内部根据底层错误和操作类型判断) - 如果必须自定义类型,
Temporary()方法体里应做具体判断,而不是硬编码return true:func (e *MyError) Temporary() bool { switch e.Err.(type) { case *net.OpError, *net.DNSError, *net.AddrError: return true default: return false } } - 注意:
os.SyscallError不实现net.Error,但它的Err字段可能是*net.OpError,需解包检查
http.Client 重试时到底看哪个错误的 Temporary()
HTTP 客户端不会直接调用你传进去的 error 的 Temporary() —— 它只关心底层 transport 抛出的错误,而且只在特定环节生效。
- 仅当
RoundTrip返回非 nil error 时,http.Client才会尝试调用其Temporary();如果 error 没实现net.Error,就默认当作致命错误 - 常见误区:在
CheckRedirect或Transport.RoundTrip中 wrap 错误后忘了保留Temporary()实现,导致本可重试的网络错误被当成永久失败 - 一个可靠做法:用
fmt.Errorf("wrap: %w", origErr)并确保origErr是实现了Temporary()的类型;%w会保留底层 error 的接口实现
为什么 io.EOF 不是临时错误,但某些 read: connection reset by peer 却是
这取决于错误发生的上下文和底层系统调用返回值,不是看错误消息字面意思。
立即学习“go语言免费学习笔记(深入)”;
-
io.EOF是正常结束信号,不属于错误分类体系里的“临时/致命”,它甚至不实现error接口的全部语义(比如不能用errors.Is(err, io.EOF)判断是否为 EOF,但它是error类型) -
read: connection reset by peer对应syscall.ECONNRESET,net.OpError对它的Temporary()返回false(因为连接已被对端强制关闭,重试无意义),但如果是write: broken pipe在某些场景下可能返回true,取决于 socket 状态和写入时机 - 真正关键的是:不要依赖错误字符串,而要通过
errors.As(err, &net.OpError{})解包后检查其Err字段类型和值
临时错误的边界其实很窄 —— 它只代表“此刻不可用,稍后可能行”,而不是“也许能行”。一旦开始靠猜错误字符串或者统一返回 true,重试逻辑就会把服务器拖垮或者掩盖真正的配置问题。










