go的http.client默认完全不重试,所有重试必须手动实现:需新建请求、区分可重试错误(如net.operror、5xx/429)、用指数退避+抖动、正确处理body重放与context生命周期。

直接用 retry.Do 或 backoff.Retry 是最省事的起点,但不加错误过滤、不设 jitter、不传 context 的调用,等于把重试当“硬重启”用——失败率没降,下游压力翻倍。
哪些错误值得重试?别让 400 错误也进重试循环
重试不是兜底,是针对临时性故障的精准响应。关键在区分错误类型,而不是看 err != nil 就 retry。
-
net.OpError(连接拒绝、超时)、url.Error中Timeout()返回 true 的情况,属于典型可重试网络错误 - HTTP 响应码必须显式检查:
resp.StatusCode >= 500 && resp.StatusCode ,且排除 <code>501 Not Implemented这类服务端不支持而非临时故障的码 -
grpc.Code只有codes.Unavailable、codes.ResourceExhausted、codes.DeadlineExceeded才触发重试;codes.NotFound或codes.InvalidArgument必须跳过 - 像
sql.ErrNoRows、json.SyntaxError、os.ErrNotExist这类确定性错误,重试只会重复失败,应包装为backoff.Permanent(err)或直接返回
为什么 retry.Attempts(3) 不等于“重试 3 次”?
retry-go 的 retry.Attempts(n) 表示「总共执行 n 次」,含首次调用。也就是说 retry.Attempts(3) = 1 次原调 + 2 次重试 —— 新手常误以为能 retry 3 回,结果少了一轮容错机会。
- 若你希望最多重试 3 次(即总共 4 次尝试),必须写
retry.Attempts(4) -
backoff/v4则更直白:backoff.WithMaxRetries(bo, 3)明确表示「最多重试 3 次」 - 两者都不自动判断错误是否可重试,必须你在操作函数里手动做
if !isRetryable(err) { return backoff.Permanent(err) } - 别依赖
retry.Delay或backoff.NewExponentialBackOff()默认值:前者默认无延迟,后者MaxElapsedTime默认 10s,可能比你的 context timeout 还长,导致卡死
context 被 cancel 后还 sleep?那是 goroutine 泄漏温床
所有阻塞操作(包括退避等待)必须受 ctx.Done() 约束。裸写 time.Sleep 或复用已 cancel 的 ctx,会导致协程永远卡在 sleep 或空转。
- 每次重试前必须先
select { case ,不能只在函数开头 check 一次 - 退避等待必须用
select+time.After,例如:select { case - gRPC 或 HTTP 调用内部也要透传这个
ctx,否则单次请求超时了,重试逻辑还在等下一轮 - 别复用同一个
context.WithTimeout(parentCtx, ...)实例:每次重试都要新建ctx, cancel := context.WithTimeout(parentCtx, perAttemptTimeout),且cancel()必须显式调用(尤其在循环内,defer cancel()会失效)
HTTP 客户端该不该在业务层写 for 循环?
在业务函数里套 for i := 0; i 是最常见也最危险的做法——它把超时控制、错误分类、退避策略全耦合进业务逻辑,后续加监控或换策略就得动核心代码。
- 正确做法是封装一个带重试的
RoundTripper,只拦截RoundTrip返回的错误,不影响其他 HTTP 调用 - 用
&http.Transport{RoundTripper: originalRT}包装原生 transport,避免污染全局http.DefaultClient - 健康检查、配置拉取这类低优先级请求如果也被重试三次,反而会放大下游压力,所以重试必须按 endpoint 或 client 隔离
- 如果你用的是
retry-go,它的retry.Do本身不感知 HTTP,你仍需在闭包里自己判断响应码和错误类型;它只负责调度,不负责决策
真正难的不是“怎么重试”,而是“什么时候不该重试”和“重试时如何不伤人”。jitter、错误白名单、per-request context、transport 层隔离——这些不是锦上添花的配置项,是生产环境里避免雪崩的必要护栏。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











