查看原文
其他

基于.NET的弹性及瞬间错误处理库Polly

DotNet 2019-08-03

(点击上方蓝字,可快速关注我们)


来源:LamondLu

cnblogs.com/lwqlun/p/8119856.html


本文基本是官方说明的翻译和总结(https://github.com/App-vNext/Polly)


什么是Polly?


Polly是一款基于.NET的弹性及瞬间错误处理库, 它允许开发人员以顺畅及线程安全的方式执行重试(Retry),断路器(Circuit),超时(Timeout),隔板隔离(Bulkhead Isolation)及后背策略(Fallback)。


Polly适用于.NET 4.0, .NET 4.5及.NET Standard 1.1(覆盖.NET Core, Mono, Xamarin.IOS, Xamarin.Android, UWP, WP 8.1+)。


安装Polly


.NET 4.0版本


Install-Package Polly.Net40Async


.NET 4.5及以上版本, .Net Standard 1.1


Install-Package Polly


弹性策略


Polly提供多种弹性策略。


重试策略


前提


程序会产生许多瞬时故障,但是在一定时间延迟之后,程序会自动纠正故障。


实现效果


允许配置自动重试。


断路器策略


前提


当系统发生严重故障时,快速响应请求失败比让用户等待要好。

避免故障系统过载有助于恢复系统。


实现效果


当系统错误超过预配置的数量,系统将断路一段时间。


超时策略


前提


超出一定时间的等待,想要得到正确的结果是不太可能的。


实现效果


保证调用者不需要等待超时。


隔板隔离


前提


当进程出现故障,多个失败的请求很容易占满服务器资源(线程/CPU)。

一个处于故障状态的下游系统,也会导致其上游系统故障。


实现效果


将严格管控故障进程,使其使用固定大小的资源池,隔离他们对其他进程的潜在影响


缓存策略


前提


一定比例的请求可能是相似的。


实现效果


从缓存中提供已知的响应。


当第一次读取的时候,将响应自动缓存起来。


后备策略


前提


当故障依然存在的时候,你打算做什么。


实现效果


当程序依然发生故障时,执行指定操作。


包装策略


前提


不同的故障需要不同的策略。包装策略即组合策略。


实现效果


允许灵活的将以上任意几种策略组合在一起。


如何使用Polly进行故障/异常处理?


Polly处理故障/异常有以下几个步骤。


  1. 指定处理的异常/故障类型


  2. [可选] 指定处理的异常返回值


  3. 指定处理策略


  4. 执行策略


指定处理异常/故障的类型


Polly使用Policy类的泛型方法Handle指定Polly需要处理异常/故障的类型。


指定单个异常类型


Policy.Handle<DivideByZeroException>()


指定带条件的异常类型


Policy.Handle<SqlException>(ex => ex.Number == 1205)


Polly也支持指定多种异常/故障类型, 这里需要使用Or方法


Policy.Handle<DivideByZeroException>().Or<ArgumentException>()


指定多个带条件的异常类型


Policy

   .Handle<SqlException>(ex =ex.Number == 1205)

   .Or<ArgumentException>(ex =ex.ParamName == "example")


Polly也支持指定内部异常


Policy

    .HandleInner<HttpResponseException>()

    .OrInner<OperationCanceledException>(ex => ex.CancellationToken == myToken)


指定处理的异常返回值


Polly除了支持处理异常/故障类型,还支持处理异常返回值。所谓的处理异常结果,就是当Polly监控的方法,返回某些特定结果时, Polly会触发异常/故障处理策略。


Polly使用Policy类的泛型方法HandleResult制定Polly需要处理的异常结果.


指定触发异常/故障处理策略的返回值


例如:当某个方法的返回值类型是HttpResposneMessage, 并且返回值的StatusCode是NotFound时,触发异常/故障处理策略。


Policy

.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.NotFound)


指定多个返回值


Policy

    .HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.InternalServerError)

    .OrResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.BadGateway)


同时指定异常类型和返回值


HttpStatusCode[] httpStatusCodesWorthRetrying = {

    HttpStatusCode.RequestTimeout, // 408

    HttpStatusCode.InternalServerError, // 500

    HttpStatusCode.BadGateway, // 502

    HttpStatusCode.ServiceUnavailable, // 503

    HttpStatusCode.GatewayTimeout // 504

}; 

HttpResponseMessage result = Policy

    .Handle<HttpResponseException>()

    .OrResult<HttpResponseMessage>(r => httpStatusCodesWorthRetrying.Contains(r.StatusCode))


指定异常处理策略


重试策略


重试一次


Policy

    .Handle<DivideByZeroException>()

    .Retry()


重试多次


Policy

    .Handle<DivideByZeroException>()

    .Retry(3)


重试多次,每次重试触发一个行为


Policy

    .Handle<DivideByZeroException>()

    .Retry(3, (exception, retryCount) =>

    {

        // do something 

    });


永久重试(直到成功)


永久重试


Policy

    .Handle<DivideByZeroException>()

    .RetryForever()


永久重试,每次重试触发一个行为


Policy

    .Handle<DivideByZeroException>()

    .RetryForever(exception =>

    {

            // do something       

    });


等待并重试


指定每个重试的间隔时间


Policy

    .Handle<DivideByZeroException>()

    .WaitAndRetry(new[]

    {

        TimeSpan.FromSeconds(1),

        TimeSpan.FromSeconds(2),

        TimeSpan.FromSeconds(3)

    });


在这个例子如果第一次出现异常,会在1秒后重试,如果依然出现异常,会在再次出现异常后2秒继续重试,以此类推,下次异常后3秒继续重试


每次重试,触发一个行为


Policy

    .Handle<DivideByZeroException>()

    .WaitAndRetry(new[]

    {

      TimeSpan.FromSeconds(1),

      TimeSpan.FromSeconds(2),

      TimeSpan.FromSeconds(3)

    }, (exception, timeSpan) => {

      // do something    

    }); 


断路器策略


在发生指定次数的异常/故障之后,断开回路


Policy

    .Handle<DivideByZeroException>()

    .CircuitBreaker(2, TimeSpan.FromMinutes(1));


发生2次异常之后,断开回路1分钟


Action<Exception, TimeSpan> onBreak = (exception, timespan) => { ... };

Action onReset = () => { ... };

CircuitBreakerPolicy breaker = Policy

    .Handle<DivideByZeroException>()

    .CircuitBreaker(2, TimeSpan.FromMinutes(1), onBreak, onReset);


发生2次异常之后,断开回路1分钟, 在触发断路时触发onBreak方法,当重置断路器时,触发onReset方法


后备策略


Policy

    .Handle<Whatever>()

    .Fallback<UserAvatar>(UserAvatar.Blank)


当程序触发异常/故障后,返回一个备用值


Policy

    .Handle<Whatever>()

    .Fallback<UserAvatar>(() => UserAvatar.GetRandomAvatar())


当程序触发异常/故障后,使用一个方法返回一个备用值


Policy

   .Handle<Whatever>()

   .Fallback<UserAvatar>(UserAvatar.Blank, onFallback: (exception, context) => 

    {

        // do something

    });


当程序触发异常/故障后,返回一个备用值,并触发一个方法


Policy

   .Handle<Whatever>()

   .Fallback<UserAvatar>(UserAvatar.Blank, onFallback: (exception, context) => 

    {

        // do something

    });


执行策略


Polly将监控DoSomething方法,如果发生DivideByZeroException异常,就使用重试策略


var policy = Policy

              .Handle<DivideByZeroException>()

              .Retry();


policy.Execute(() => DoSomething());


向Polly上下文中传递任意值


var policy = Policy

    .Handle<DivideByZeroException>()

    .Retry(3, (exception, retryCount, context) =>

    {

        var methodThatRaisedException = context["methodName"];

        Log(exception, methodThatRaisedException);

});


policy.Execute(

    () => DoSomething(),

    new Dictionary<string, object>() {{ "methodName", "some method" }}

);


看完本文有收获?请转发分享给更多人

关注「DotNet」,提升.Net技能 


淘口令复制以下红色内容,再打开手淘即可购买

范品社,使用¥极客T恤¥抢先预览(长按复制整段文案,打开手机淘宝即可进入活动内容)

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存