基於.NET的彈性及瞬間錯誤處理庫Polly


本文基本是官方說明的翻譯和總結(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" }}
);


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM