重试
翻译自PollyWiki:https://github.com/App-vNext/Polly/wiki/Retry
语法
RetryPolicy retry = Policy .Handle<HttpRequestException>() .Retry(3);
上面的示例将创建一个重试策略,如果某个操作失败且该策略处理了异常,则该重试策略将至多重试三次。
有关完整的重试语法和重载(包括retry-forever、wait-and-retry和相关变量),请参见https://github.com/App-vNext/Polly#retry。
给出的语法示例是sync;对于异步操作也存在类似的异步重载:请参阅
[readme] https://github.com/App-vNext/Polly/blob/master/README.md#asynchronous-support
和
[wiki] https://github.com/App-vNext/Polly/wiki/Asynchronous-action-execution
。
Polly重试机制的工作流程
当一个动作通过策略执行时:
-
重试策略尝试在. execute(…)(或类似)委托中传递的操作。
-
如果操作成功执行,返回值(如果相关)并退出策略。
-
如果该操作抛出未处理的异常,则会重新抛出该异常并退出策略:不再做进一步尝试。
-
-
如果该动作抛出一个已处理的异常(或者当处理错误时:返回一个已处理的错误),策略:
-
对异常/故障 发生的次数进行计数
-
检查其他的重试是否满足重试条件(重试次数要小于配置的最大重试次数等)
-
如果不符合重试条件,则重新抛出异常(/返回错误)并终止策略。
-
符合重试条件,则策略继续执行:
-
对于等待-重试策略,从提供的休眠时间配置计算等待的时间
-
执行onRetry委托(如果配置了)
-
对于wait-and-retry策略,等待计算的持续时间。
-
返回循环的开始,再次重试执行该操作。
-
-
-
总尝试次数
执行该操作的总尝试次数是1加上配置的重试次数。例如,如果策略配置为. retry(3),则最多尝试4次:第一次尝试,再加上最多重试3
配置策略的委托
onRetry / onRetryAsync
一个可选的“onRetry”或“onRetryAsync”委托,可以在许多策略上配置。委托的输入参数可以是:
int retryCount
-
1, 在第一次尝试之后,第一次重试之前
-
2, 在第一次重试之后,第二次重试之前(依次类推)
####Exception
or DelegateResult<TResult>
Exception
: 触发重试的异常,对于只处理异常而不处理结果值的重试策略 or DelegateResult<TResult>
:对于处理异常和/或结果的重试策略,前一次执行的结果触发了重试:
-
DelegateResult<TResult>.Exception
如果处理的异常包含此异常,则触发了重试;其他不触发的情况此值应该为null
-
DelegateResult<TResult>.Result
如果处理结果包含此结果值,则触发重试;其他不触发的情况此值应该为默认值default(TResult)
TimeSpan
WaitAndRetry策略的wait-before-next-try的持续时间。onRetry/Async委托在下次重试之前被调用。
Context
该策略的执行上下文,唯一(多次重试使用的是同一个上下文)。
sleepDurationProvider
一些WaitAndRetry策略使用“sleepDurationProvider”作为函数,以提供特定重试的等待时间。 所有版本的' sleepDurationProvider '都有一个返回参数' TimeSpan ',即下一次重试之前等待的时间。 委托的输入参数可以是:
-
int retryCount
: 参考上文. -
Context
: 参考上文. -
Exception
orDelegateResult<TResult>
: 参考上文.
Exponential backoff(指数回退)
一种常见的重试策略是指数回退:这允许最初快速地进行重试,然后以逐渐变长的间隔进行重试,以避免在子系统可能出现故障时使用重复频繁的调用打击子系统。 指数回退可以通过手动配置waits-between-retries来实现(如果你想要一些自定义或易于阅读的方案,这很有用):
Policy .Handle<SomeExceptionType>() .WaitAndRetry(new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(4), TimeSpan.FromSeconds(8), TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(30) });
或者是计算:
Policy .Handle<SomeExceptionType>() .WaitAndRetry(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)) );
上面的代码演示了如何从头构建一个常见的指数补偿模式,但我们了不起的社区也为这个项目提供了简洁的辅助方法和其他一些常见wait-and-retry示例:参考[Polly.Contrib.WaitAndRetry] (https://github.com/Polly-Contrib/Polly.Contrib.WaitAndRetry/)!
Jitter
在非常高的吞吐量场景中,向等待和重试策略中添加抖动可能是有益的,以防止重试聚集成进一步的负载峰值。
在运行时动态调整重试前延迟
重试间隔可以随着策略的运行而动态调整,有以下方式:
-
使用带有' Func<…,TimeSpan> sleepDurationProvider '的重载;
-
使用带有' IEnumerable<TimeSpan> sleepDurations '参数的重试重载,并使用' yield return '实现来返回动态变化的值。
对于偶尔调整策略的参数(在其他情况下保持相当不变),也可以考虑通过' PolicyWrap '建议的运行时动态重新配置方法。
RetryAfter:当响应指定要等待多长时间时
一些系统将等待多长时间作为返回的故障响应的一部分。这通常表示为带有429响应代码的' Retry-After '报头。
这可以使用带有sleepDurationProvider
的 WaitAndRetry/Forever/Async(...)
重载,sleepDurationProvider
带有需要处理的错误/异常等输入参数。(重载实例; 讨论与样例代码).
一些SDK包定制化修改了RetryAfter的响应与异常处理机制:例如,底层Azure CosmosDB架构(RESTful 风格的api接口)发送一个429响应代码(太多的请求)并带有x-ms-retry-after-ms头,但Azure 客户端SDK是抛出DocumentClientException通过回调代码来处理 RetryAfter。可以使用相同的重载来处理这些。对于使用Azure客户端SDK的CosmosDB,这里有一些示例代码.
重试刷新授权
在第三方系统中,授权会周期性失效,所以可以使用重试策略来保持第三方系统的授权,示例:
var authorisationEnsuringPolicy = Policy .HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.Unauthorized) .RetryAsync( retryCount: 1, // Consider how many retries. If auth lapses and you have valid credentials, one should be enough; too many tries can cause some auth systems to block or throttle the caller. onRetryAsync: async (outcome, retryNumber, context) => FooRefreshAuthorizationAsync(context), /* more configuration */); var response = authorisationEnsuringPolicy.ExecuteAsync(context => DoSomethingThatRequiresAuthorization(context), cancellationToken);
' FooRefreshAuthorizationAsync(…)'方法可以获得一个新的授权令牌,并使用' poly . context '将其传递给通过策略执行的委托。有关工作示例,请参阅Jerrie Pelser的博客:使用Polly刷新谷歌访问令牌。
如果您希望将重试重新认证(如上所示)与重试短暂故障(try-for-transient-fault)结合使用,则可以分别表示每个重试策略,然后与' PolicyWrap '结合使用。
线程安全
通过重试策略对' . execute(…)'(或类似的)的每次调用都维护其自己的私有状态。因此,可以在多线程环境中安全地重用重试策略。
重试策略的内部操作是线程安全的,但这并不能神奇地使您通过策略执行的委托是线程安全的:如果您通过策略执行的委托不是线程安全的,那么它们仍然不是线程安全的。