使用 DecorlatedJitterBackoff 的最大重试次数

2023-11-29

我正在使用 polly DecorlatedJitterBackoff 策略来重试 http 请求。我的用例是,当 timeSpan 达到 300 秒时,它应该每 300 秒重试 int.maximum 次数。

我正在尝试使用以下代码来实现此目的。我使用了 int.MaxValue ,它给出了超出范围的异常,所以我使用 2100000000 。该代码可以工作,但执行起来需要太多时间。请建议一种有效的方法来实现这一点?

    private static readonly List<int> ExceptionCodes = new List<int> { 408, 429, 500, 503, 504, 520 };
    var delay = Backoff.DecorrelatedJitterBackoffV2(medianFirstRetryDelay: TimeSpan.FromMilliseconds(2500), 10);

    var decorrelatedJitterDelay = this.GetTimeSpanList(delay.ToArray());

    this.RetryPolicy = Policy.HandleResult<HttpResponseMessage>(r => ExceptionCodes.Contains((int)r.StatusCode))
     .WaitAndRetryAsync(decorrelatedJitterDelay);

     var policyResult = this.RetryPolicy.ExecuteAndCaptureAsync(() => this.RequestServer(equipmentId));



     private IEnumerable<TimeSpan> GetTimeSpanList(TimeSpan[] delay)
            {
                var index = 0;
                var timeSpanList = new List<TimeSpan>();
                foreach (var time in delay)
                {
                    if (time > TimeSpan.FromSeconds(300))
                    {
                       var timeDelay = TimeSpan.FromSeconds(300);
                       delay[index] = timeDelay;
                       timeSpanList.Add(delay[index]);
                    }

                    index++;
                }

               // 2100000000  is the maximum capacity of List<>.
                for (int i = index; i < 2100000000 - index; i++)
                {
                    timeSpanList.Add(TimeSpan.FromSeconds(300));
                }

                return timeSpanList;
            }

提前致谢


为了实现所需的行为,需要修改一些小事情。

sleepDurationProvider

如果你看一下 的定义sleepDurationProvider的参数WaitAndRetry方法,然后你可以看到它是一个函数,它将根据一些输入(如当前重试计数、上下文等)生成一个 timeSpan 。

Func<int, TimeSpan>
Func<int, DelegateResult<HttpResponseMessage>, Context, TimeSpan>
...

因此,我们可以按需计算它们,而不是提前指定每个睡眠持续时间。这真的很好,因为我们可以利用yield return通过考虑之前的时间跨度来按需创建新的时间跨度。

这是一个示例方法,它将按需生成 TimeSpan:

private static IEnumerable<TimeSpan> GetDelay()
{
    TimeSpan fiveMinutes = TimeSpan.FromMinutes(5);
    var initialBackOff =  Backoff.DecorrelatedJitterBackoffV2(medianFirstRetryDelay: TimeSpan.FromMilliseconds(2500), 10);
    foreach (var delay in initialBackOff.Where(time => time < fiveMinutes))
    {
        yield return delay;
    }

    while (true)
    {
        yield return fiveMinutes;
    }
}
  • 我们生成前 10 个持续时间DecorrelatedJitterBackoffV2.
  • 我们过滤掉那些超过 5 分钟的内容(Where(time => time < fiveMinutes))
  • 在重试策略耗尽初始退避时间后,我们将始终在 5 分钟后返回。
  • Please note that this Iterator never returns.
    • 但因为它是按需消耗的,所以这不是一个大问题。

让我们通过查询前 20 个睡眠持续时间来测试此方法:

foreach (var ts in GetDelay().Take(20))
{
    Console.WriteLine(ts.TotalSeconds);
}

输出将是:

0.5985231
4.0582524
5.1969925
15.4724158
16.4869722
15.8198397
75.7497326
118.5080045
272.2401684
300
300
300
300
300
300
300
300
300
300
300

WaitAndRetry vs WaitAndRetryForever

尽管前者确实有几个接受一个重载IEnumerable<TimeSpan>参数我不会推荐它。大多数重载需要显式retryCount这就是为什么在大多数人看来,这个函数被认为是预定义的、有限重试执行器。

我建议使用WaitAndRetryForever因为它表达了意图。无需查看睡眠持续时间生成器,我们想要什么就很明显了。

这里是精致的RetryPolicy定义:

var sleepDurations = GetDelay().GetEnumerator();
var retryPolicy = Policy
    .HandleResult<HttpResponseMessage>(r => ExceptionCodes.Contains((int)r.StatusCode))
    .WaitAndRetryForever(retry =>
    {
        sleepDurations.MoveNext();
        return sleepDurations.Current;
    });
  • WaitAndRetryForever没有任何接受的重载IEnumerable<TimeSpan>这就是为什么我们必须使用一些样板代码。
  • sleepDurations是一个迭代器,每次重试策略需要计算睡眠持续时间时,我们都会向前移动。
  • 该算法没有考虑当前的重试次数(retry)所以如果你愿意的话你可以使用那里的丢弃物(.WaitAndRetryForever(_ => ...)

Execute vs ExecuteAsync vs ExecuteCapture vs ...

根据您如何指定您的保单,您可以致电Execute or ExecuteAsync。前者用于同步操作,后者用于异步 I/O 操作。

RetryPolicy<HttpResponseMessage> retryPolicy = Policy.....WaitAndRetryForever(...
retryPolicy.Execute(() => ....);

or

AsyncRetryPolicy<HttpResponseMessage> retryPolicy = Policy.....WaitAndRetryForeverAsync(...
await retryPolicy.ExecuteAsync(async () => await ...)
  • ExecuteAsync预计有一个异步函数(Func<Task<...>>)这就是为什么我们必须使用async() => await ...
  • ExecuteAsync确实返回Task所以也应该等待。

If your RequestServer是同步方法则使用前者,如果是异步则使用后者。

我还鼓励您使用简单的Execute代替ExecuteAndCapture如果您对政策相关信息不感兴趣,只对结果本身感兴趣。

var result = retryPolicy.Execute(() => this.RequestServer(equipmentId));
sleepDurations.Dispose();
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 DecorlatedJitterBackoff 的最大重试次数 的相关文章

随机推荐