用 retry.Do 封装 HTTP 请求最省事,需设重试次数、指数退避、错误过滤及 context 控制,避免盲目重试 5xx 或忽略超时与并发安全问题。

用 retry.Do 封装 HTTP 请求最省事
Go 标准库不带重试,但社区有成熟方案:github.com/avast/retry-go 是目前最轻量、无依赖、API 清晰的选择。它不侵入业务逻辑,只负责控制重试节奏和条件。
常见错误是自己手写 for 循环 + time.Sleep:容易漏掉上下文取消、指数退避、错误过滤,还可能把临时错误(如 net.ErrTimeout)和永久错误(如 404)一视同仁重试。
- 用
retry.Do包裹整个请求逻辑,失败时自动重试 - 必须传
retry.Attempts(n),否则默认无限重试(危险) - 加
retry.Delay(1 * time.Second)避免瞬间打爆下游;配合retry.DelayType(retry.BackOffDelay)启用指数退避 - 用
retry.LastErrorOnly(true)让最终返回的 error 只是最后一次失败原因,避免干扰日志分析
HTTP 错误要分类判断,不是所有 5xx 都该重试
盲目对所有 5xx 重试会放大问题:比如 501 Not Implemented 或 503 Service Unavailable(且响应头带 Retry-After)行为完全不同。关键在 retry.RetryIf 回调里做细粒度判断。
典型场景是调第三方 API,你得区分网络层错误(可重试)、服务端逻辑错误(不该重试)、认证失败(重试没用)。
-
url.Error和net.OpError通常可重试(连接超时、拒绝连接) -
*http.Response的StatusCode属于500、502、503、504才考虑重试;400、401、403、404直接退出 - 别忽略响应 body:有些服务在
500时返回 JSON 提示 “rate limit exceeded”,这种也该归为不可重试
Context 控制超时和取消不能只靠外层 http.Client.Timeout
仅设 http.Client.Timeout 只能限制单次请求耗时,无法限制“整个重试过程”的总时间。用户点击取消、上游 deadline 到期、K8s probe 超时,都需要统一通过 context.Context 传递。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
错误做法是把 context 传进 retry.Do 却没在每次重试中检查 ctx.Err() —— retry-go 默认不感知 context,必须显式用 retry.Context(ctx) 注入。
- 创建带 deadline 的 context:
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - 务必传
retry.Context(ctx),否则重试会无视超时继续跑 - 在 retry 函数体内部仍需手动检查
if ctx.Err() != nil { return ctx.Err() },防止某次请求卡死阻塞整个重试流程 - 记得调
cancel(),尤其在 defer 里,避免 goroutine 泄漏
自定义 backoff 和 jitter 要小心并发安全
如果用 retry.BackOffDelay,底层基于 time.Duration 算指数增长,但多个请求并发时若共享同一个 delay 计算器,可能因非原子操作导致退避时间错乱(比如本该 2s 却变成 0.5s)。更隐蔽的问题是 jitter(随机抖动)没加,导致重试请求扎堆打到后端。
简单方案是每次重试都重新计算 delay,而不是复用变量:
retry.DelayType(func(n uint, config retry.Config) time.Duration {
base := time.Second * time.Duration(1
- 不要在闭包外定义并复用
rand.Rand实例,多 goroutine 下非并发安全;改用math/rand.New(rand.NewSource(time.Now().UnixNano()))每次新建 - 如果服务端明确支持
Retry-After响应头,优先用它替代本地计算的 backoff - 首次重试间隔建议不低于 100ms,太短等于无效重试,还增加负载
重试机制真正难的不是代码长度,而是错误分类的颗粒度和上下文生命周期的精准控制——这两点漏一个,就容易把“高可用”做成“高扰动”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










