94

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

 6 years ago
source link: https://www.cnblogs.com/lwqlun/p/8119856.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

本文基本是官方说明的翻译和总结(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. 指定处理策略

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

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" }}
);

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK