← 中文资源

简体中文版本

带抖动的指数退避重试

对可重试操作执行带上限的指数退避,加入随机抖动避免同时重试,支持上下文取消,并识别不可重试的永久错误。

阅读英文原文

功能说明

Do 对可重试操作执行有限次尝试。每次失败后按 BaseDelay << shift 计算指数退避,用 MaxDelay 封顶,再按 Jitter 做上下浮动,减少多个客户端同时重试造成的惊群。每次等待都监听上下文取消;如果错误被标记为永久错误,立即停止并返回包装后的原因。

关键代码

shift := attempt - 1
if shift > 6 {
	shift = 6
}
delay := cfg.BaseDelay * time.Duration(1<<shift)
if cfg.MaxDelay > 0 && delay > cfg.MaxDelay {
	delay = cfg.MaxDelay
}

位移上限防止尝试次数很大时整数溢出,封顶则保证延迟不会无限增长。

使用注意

  • isRetryable 通过 errors.Is(err, errPermanent) 判断,业务错误应使用 %w 包装哨兵错误。
  • 随机抖动使用全局 math/rand,并发安全,但结果不可复现;测试时可注入固定随机源。
  • 只重试幂等操作,非幂等写入需要服务端幂等键。
  • 上下文取消会中断等待并返回 ctx.Err(),不会继续下一次尝试。
  • 最终错误用 %w 包装最后一次失败,调用方仍可判断根因。

验证方式

示例中前两次返回临时错误,第三次成功,输出 transient attempts=3 err=<nil>;第二次运行返回永久错误,只尝试一次。把 Attempts 改为 1 或去掉 Jitter,可分别验证次数与固定延迟。