.NetCore 实践——Polly[二]
polly 组件包:
polly 功能包
polly.Extensions.Http
专门针对http的扩展包
Miscrosoft.Extension.Http.Polly
看到这个名字,那么99%是针对官方.net core的扩展包,是HttpClientFactory 的扩展。
polly有下面一些功能:
失败重试
服务熔断
超时处理
舱壁处理
缓存策略
失败降级
组合策略
舱壁处理 是什么呢?
这个是限流功能,服务定义最大的流量和队列,避免请求量过大而崩溃。
组合策略,就是对上面的功能可以自由组合。
使用步骤
定于要处理的异常类型和返回值
定义要处理的工作
使用定义的策略来执行代码
polly的源代码:
https://github.com/App-vNext/Polly
polly 针对http的扩展包:
https://github.com/App-vNext/Polly.Extensions.Http
适用场景
服务"失败"是短暂的,可自愈的。
针对这种http请求,无状态的是非常适用的。
服务是"幂等"的,重复调用不会产生副作用
这个幂等可以简单为多次执行,并不会影响到最初达到的效果。(比如说个人查询,查询多次的话,效果是相同的。)
具体场景:
服务闪断
部分节点不可用
在使用重试过程中,最好达到下面几个要求:
设置失败次数
设置有步长策略的失败等待间隔
设置降级响应
设置断路器
前面说过polly 是针对httpClientFactory 的扩展,当监听到AddTransientHttpErrorPolicy
错误的时候,那么可以启动对应的策略进行重试,RetryAsync
就是polly的扩展,里面设置重试次数20次。
var s = services
.AddHttpClient("test")
.SetHandlerLifetime(TimeSpan.FromMinutes(5))
.AddTransientHttpErrorPolicy(p=>p.RetryAsync(20));
看下AddTransientHttpErrorPolicy,其实个人感觉RetryAsync倒是没有必要去看,看AddTransientHttpErrorPolicy 是为了知道啥时候会触发这个重试,以及知道如何去定义我们自己的Policy。
public static IHttpClientBuilder AddTransientHttpErrorPolicy(
this IHttpClientBuilder builder,
Func<PolicyBuilder<HttpResponseMessage>, IAsyncPolicy<HttpResponseMessage>> configurePolicy)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (configurePolicy == null)
{
throw new ArgumentNullException(nameof(configurePolicy));
}
var policyBuilder = HttpPolicyExtensions.HandleTransientHttpError();
// Important - cache policy instances so that they are singletons per handler.
var policy = configurePolicy(policyBuilder);
builder.AddHttpMessageHandler(() => new PolicyHttpMessageHandler(policy));
return builder;
}
从名字中可以看到HandleTransientHttpError就是处理异常的。
public static PolicyBuilder<HttpResponseMessage> HandleTransientHttpError()
{
return Policy<HttpResponseMessage>.Handle<HttpRequestException>().OrTransientHttpStatusCode();
}
这里表示HttpRequestException 会触发,或者OrTransientHttpStatusCode一些状态码下会触发,看下OrTransientHttpStatusCode。
public static PolicyBuilder<HttpResponseMessage> OrTransientHttpStatusCode(
this PolicyBuilder<HttpResponseMessage> policyBuilder)
{
if (policyBuilder == null)
throw new ArgumentNullException(nameof (policyBuilder));
return policyBuilder.OrResult(HttpPolicyExtensions.TransientHttpStatusCodePredicate);
}
查看HttpPolicyExtensions.TransientHttpStatusCodePredicate:
private static readonly Func<HttpResponseMessage, bool> TransientHttpStatusCodePredicate = (Func<HttpResponseMessage, bool>) (response => response.StatusCode >= HttpStatusCode.InternalServerError || response.StatusCode == HttpStatusCode.RequestTimeout);
这里面表示 HttpStatusCode.InternalServerError(500)和HttpStatusCode.RequestTimeout(408) 会进行重试。
同样可以设置间隔时间进行重试:
services.AddHttpClient("ds")
.SetHandlerLifetime(TimeSpan.FromMinutes(5))
.AddTransientHttpErrorPolicy(p => p.WaitAndRetryAsync(20, i => TimeSpan.FromSeconds(2)));
还可以使用WaitAndRetryForever 表示一直重试,直到成功,看需求。
同样也可以自定义一些状态或者一些情况,做一些事情:
var reg=services.AddPolicyRegistry();
reg.Add("retryforever", Policy.HandleResult<HttpResponseMessage>(message =>
{
return message.StatusCode == System.Net.HttpStatusCode.Created;
}).RetryForever());
services.AddHttpClient("test")
.AddPolicyHandlerFromRegistry("retryforever");
推荐做法
/// <summary>
/// Polly:Http 重试策略
/// </summary>
/// Usage:services.AddHttpClient<,>().AddPolicyHandler(GetRetryPolicy())
/// <returns></returns>
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == HttpStatusCode.NotFound)
.WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)) // exponential back-off: 2, 4, 8 etc
+ TimeSpan.FromMilliseconds(new Random().Next(0, 1000)) // plus some jitter: up to 1 second (将抖动策略添加到重试策略)
);
}
// ... 可以根据实际情况定义不同的重试策略
// Program.cs中
services.AddHttpClient("test")
.AddPolicyHandler(GetRetryPolicy())
// .AddPolicyHandler(xxxxPolicy())
;
Subscribe to my newsletter
Read articles from shiney directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by