为了实现所需的行为,需要修改一些小事情。
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();