如何使用Golang实现并发请求重试机制_处理网络请求失败

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

在 Go 中实现并发请求重试机制,核心是结合 context 控制超时与取消、用 sync.WaitGrouperrgroup 协调并发、配合指数退避(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)。

  • 定义重试参数: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),立即返回,不进入重试循环