← 中文资源

简体中文版本

异步指数退避重试

编写泛型异步重试函数,按尝试次数指数增加等待时间并加入抖动,最终返回带尝试次数与最后错误信息的结构体。

阅读英文原文

功能说明

retry 是一个泛型异步重试函数,接受尝试次数、基础延迟和一个返回 future 的闭包。每次失败后按 base * 2^shift 计算等待时间,shift 最多 5,避免延迟无限增长,再加上按尝试次数计算的抖动毫秒数。次数耗尽后返回 RetryError,其中包含总尝试次数和最后一次错误文本。

关键代码

for attempt in 1..=attempts {
    match operation(attempt).await {
        Ok(value) => return Ok(value),
        Err(error) => {
            last = error.to_string();
            if attempt < attempts {
                let shift = (attempt - 1).min(5);
                let delay = base * (1u32 << shift) + Duration::from_millis(jitter_ms);
                sleep(delay).await;
            }
        }
    }
}

闭包参数是当前尝试序号,便于调用方在日志或幂等键中使用。

使用注意

  • 闭包类型是 FnMut,可以捕获并修改外部计数器,但同一个 future 不能并发复用。
  • 抖动是确定性的,测试可复现;生产环境通常改用随机抖动打散重试。
  • 只重试可恢复错误,参数校验失败等永久错误应在闭包内直接返回,避免浪费次数。
  • 没有整体超时预算,长时间重试可能超过调用方容忍时间,外层应再包 timeout。
  • 最后一次失败不再等待,直接构造错误返回。

验证方式

示例前两次返回错误,第三次成功,输出应为 Ok(3);把成功条件改为始终失败,可观察 RetryError { attempts: 4, last: ... }。调整 base 可测量总耗时变化。