Go并发请求重试需用context控制超时与取消、sync.WaitGroup或errgroup协调、指数退避策略;每个请求应绑定独立子context,如ctx, cancel := context.WithTimeout(parentCtx, 5*time.Second),并用select等待响应或超时。
在 Go 中实现并发请求重试机制,核心是结合 context 控制超时与取消、用 sync.WaitGroup 或 errgroup 协调并发、配合指数退避(exponential backoff)策略重试失败请求。关键不是“无限重试”,而是“可控、可中断、不压垮服务”。
每次 HTTP 请求都应绑定独立的 context,防止某次卡死拖垮整体。不要复用全局 context,也不要忽略 cancel 函数。
ctx, cancel := context.WithTimeout(parentCtx, 5*time.Second)
select 等待响应或超时:select { case resp :=
cancel()(尤其在提前返回时),避免 goroutine 泄漏golang.org/x/sync/errgroup 天然支持 context 取消传播,适合并发发请求并统一等待结果或错误。
g, gCtx := errgroup.WithContext(ctx)
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)))
delay += time.Duration(rand.Int63n(int64(delay)/10))
select 等待延迟或 context 取消:case 或 case
不是所有错误都该重试。比如 400 Bad Request、401 Unauthorized、403 Forbidden、404 Not Found 属于客户端问题,重试无意义;而 429 Too Many Requests、5xx 错误、连接超时、DNS 解析失败才值得重试。
if resp.StatusCode >= 500 || resp.StatusCode == 429
errors.Is(err, context.DeadlineExceeded) || errors.Is(err, syscall.ECONNREFUSED) || strings.Contains(err.Error(), "timeout")