Go并发请求重试需用context控制超时与取消、sync.WaitGroup或errgroup协调、指数退避策略;每个请求应绑定独立子context,如ctx, cancel := context.WithTimeout(parentCtx, 5*time.Second),并用select等待响应或超时。

在 Go 中实现并发请求重试机制,核心是结合 context 控制超时与取消、用 sync.WaitGroup 或 errgroup 协调并发、配合指数退避(exponential backoff)策略重试失败请求。关键不是“无限重试”,而是“可控、可中断、不压垮服务”。
用 context.WithTimeout + select 控制单次请求生命周期
每次 HTTP 请求都应绑定独立的 context,防止某次卡死拖垮整体。不要复用全局 context,也不要忽略 cancel 函数。
- 为每个请求创建带超时的子 context:
ctx, cancel := context.WithTimeout(parentCtx, 5*time.Second) - 发起请求后,用
select等待响应或超时:select { case resp := - 务必调用
cancel()(尤其在提前返回时),避免 goroutine 泄漏
用 errgroup.Group 管理并发 + 可取消重试
golang.org/x/sync/errgroup 天然支持 context 取消传播,适合并发发请求并统一等待结果或错误。
- 初始化:
g, gCtx := errgroup.WithContext(ctx) - 对每个 URL 启动一个 goroutine:
g.Go(func() error { return doRequestWithRetry(gCtx, url) }) - 主协程调用
g.Wait(),任一请求失败或 context 被取消,其余请求自动中止
实现带退避的重试逻辑(不阻塞主流程)
重试不能简单 time.Sleep(1 * time.Second),要避免雪崩式重试。推荐从 100ms 开始,每次翻倍,上限设为 2–5 秒,并加入随机抖动(jitter)。
立即学习“go语言免费学习笔记(深入)”;
- 定义重试参数:
maxRetries := 3; baseDelay := 100 * time.Millisecond - 循环内计算延迟:
delay := time.Duration(float64(baseDelay) * math.Pow(2, float64(attempt))) - 添加 10%–30% 随机抖动:
delay += time.Duration(rand.Int63n(int64(delay)/10)) - 用
select等待延迟或 context 取消:case 或case
区分可重试错误 vs 不可重试错误
不是所有错误都该重试。比如 400 Bad Request、401 Unauthorized、403 Forbidden、404 Not Found 属于客户端问题,重试无意义;而 429 Too Many Requests、5xx 错误、连接超时、DNS 解析失败才值得重试。
- 检查 HTTP 状态码:
if resp.StatusCode >= 500 || resp.StatusCode == 429 - 检查底层错误:
errors.Is(err, context.DeadlineExceeded) || errors.Is(err, syscall.ECONNREFUSED) || strings.Contains(err.Error(), "timeout") - 对明确不可重试的错误(如 400、401),立即返回,不进入重试循环










