您应该将chain 重试和超时策略合并为一个组合策略。
你有两个选择:
Wrap方法
.AddPolicyHandler(retryPolicy.Wrap(timeoutPolicy))
-
timeoutPolicy 是内部策略,因此它分别适用于每次尝试
-
retryPolicy 是外部策略,因此它是超时策略的首要任务
- 注意订购事项(我会在后面详细介绍)
.AddPolicyHandler(Policy.Wrap(retryPolicy,timeoutPolicy))
- 第一个参数是最外层的策略
- 最后一个参数是最内部的策略
顺序很重要
您应该知道,以下两种组合策略非常不同:
Policy.Wrap(retryPolicy,timeoutPolicy)
Policy.Wrap(timeoutPolicy, retryPolicy)
- 在第一种情况下,您有一个本地超时,它适用于每次重试尝试
- 在第二种情况下,您有一个 全局 超时,该超时应用于整个重试活动
推进这个想法,您可以通过定义全局超时来避免设置 HttpClient 的 Timeout 属性:
var localTimeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(10);
var globalTimeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(60);
var resilientStrategy = Policy.Wrap(globalTimeoutPolicy, retryPolicy, localTimeoutPolicy);
serviceCollection.AddHttpClient("GitHub", client =>
{
client.BaseAddress = new Uri("https://api.github.com/");
client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
})
.AddPolicyHandler(resilientStrategy);
更新 #1 乐观超时
Polly 的超时确实支持 optimistic 和 pessimistic 超时。换句话说,Polly 可以尝试取消那些确实预期 CancellationToken(乐观)的待包装方法以及那些不预期(悲观)的方法。默认是前者。
在乐观的情况下,您有两种选择:
await policy.ExecuteAsync(
async ct => await httpClient.SendAsync(..., ct),
CancellationToken.None);
await policy.ExecuteAsync(
async ct => await httpClient.SendAsync(..., ct),
cancellationSource.Token);
如果您在启动期间注册您的命名/键入客户端,那么您只能使用第一个选项。因为policy.ExecuteAsync 将代表您(隐式)被调用。
如果您注册了一个类型化的客户端并在该客户端中定义了策略,那么您就是在显式调用ExecuteAsync,您可以在其中决定使用哪个版本。