弹性和瞬态故障处理库Polly介绍
前言
本節我們來介紹一款強大的庫Polly,Polly是一種.NET彈性和瞬態故障處理庫,允許我們以非常順暢和線程安全的方式來執諸如行重試,斷路,超時,故障恢復等策略。 Polly針對對.NET 4.0,.NET 4.5和.NET Standard 1.1以及.NET Core實現,該項目作者現已成為.NET基金會一員,項目一直在不停迭代和更新,項目地址【https://github.com/App-vNext/Polly】,你值得擁有。接下來我們以.NET Framework ?4.5來演示它的強大功能。
Introduce Polly
我們在下載Polly包,最新版本為5.3.1,如下:
該庫實現了七種恢復策略,下面我一一為您來介紹。
重試策略(Retry)
重試策略針對的前置條件是短暫的故障延遲且在短暫的延遲之后能夠自我糾正。允許我們做的是能夠自動配置重試機制。
斷路器(Circuit-breaker)
斷路器策略針對的前置條件是當系統嚴重掙扎時,快速失敗比讓用戶/呼叫者等待更好。保護故障系統免受過載,可以幫助它恢復。
超時(Timeout)
超時策略針對的前置條件是超過一定的等待時間,想要得到成功的結果是不可能的,保證調用者不必等待超時。
隔板隔離(Bulkhead Isolation)
隔板隔離針對的前置條件是當進程出現故障時,備份的多個失敗的呼叫可以輕松地在主機中對資源(例如線程/ CPU)進行漫游。下游系統故障也可能導致上游“備份”失敗的呼叫。這兩個風險都是一個錯誤的過程,導致更廣泛的系統。
緩存(Cache)
緩存策略針對的前置條件是數據不會很頻繁的進行更新,為了避免系統過載,首次加載數據時將響應數據進行緩存,如果緩存中存在則直接從緩存中讀取。
反饋(Fallback)
操作仍然會失敗,也就是說當發生這樣的事情時我們打算做什么。也就是說定義失敗返回操作。
策略包裝(PolicyWrap)
策略包裝針對的前置條件是不同的故障需要不同的策略,也就意味著彈性靈活使用組合。
幾種策略使用
一旦從事IT就得警惕異常并友好擁抱異常而非不聞不問,這個時候我們利用try{}catch{}來處理。
try{ ? ? ? ? ? ? ? ?var a = 0; ? ? ? ? ? ? ? ?var b = 1 / a;} ? ? ? ? ? ?catch (DivideByZeroException ex){ ? ? ? ? ? ? ? ?throw ex;}若我們想重試三次,此時我們只能進行循環三次操作。我們只能簡單進行處理,自從有了Polly,什么重試機制,超時都不在話下,下面我們來簡短介紹各種策略。Polly默認處理策略需要指定拋出的具體異常或者執行拋出異常返回的結果。處理單個類型異常如下:
Policy.Handle<DivideByZeroException>()上述異常指嘗試除以0,下面我們演示下具體使用,我們嘗試除以0并用Polly指定該異常并重試三次。
static int Compute(){ ? ? ? ? ? ?var a = 0; ? ? ? ?? ? ? ? ? ? ? ? ?? ?return 1 / a;}
如果我們想指定處理多個異常類型通過OR即可。
Policy.Handle<DivideByZeroException>().Or<ArgumentException>()當然還有更加強大的功能,比如在微信支付時,微信回調我們的應用程序時,此時若失敗,想必微信那邊也會做重試機制,例如隔一段時間重試調用一次,重復調用幾次后仍失敗則不再回調。我們利用Polly則可以演示等待重試機制。
/// <summary>/// 拋出異常 ? ? ? ?/// </summary>static void ZeroExcepcion(){ ? ? ? ? ? ?throw new DivideByZeroException();}我們講完默認策略和重試策略,再來看看反饋策略,翻譯的更通俗一點則是執行失敗后返回的結果,此時要為Polly指定返回類型,然后指定異常,最后調用Fallback方法。
static string ThrowException(){ ? ? ? ? ? ?throw new Exception();}包裹策略說到底就是混合多種策略,并執行。
var fallBackPolicy =Policy<string>.Handle<Exception>().Fallback("執行失敗,返回Fallback"); ? ? ? ? ? ?var fallBack = fallBackPolicy.Execute(() =>{ ? ? ? ? ? ? ? ?return ThrowException();});Console.WriteLine(fallBack); ? ? ??? ? ?var politicaWaitAndRetry = Policy<string>.Handle<Exception>().Retry(3, (ex, count) =>{Console.WriteLine("執行失敗! 重試次數 {0}", count);Console.WriteLine("異常來自 {0}", ex.GetType().Name);}); ? ? ? ?
? ?var mixedPolicy = Policy.Wrap(fallBackPolicy, politicaWaitAndRetry); ? ? ? ?
? ?var mixedResult = mixedPolicy.Execute(ThrowException);Console.WriteLine($"執行結果: {mixedResult}");
至此關于Polly的基本介紹就已結束,該庫還是非常強大,更多特性請參考上述github例子,接下來我們來看看兩種具體場景。
ASP.NET Web APi使用Polly重試機制
在Polly v4.30中以上可以利用HandleResult指定返回結果,如下:
Policy.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.NotFound)基于此我們完全可以利用執行Web APi中的響應策略,如下:
public readonly RetryPolicy<HttpResponseMessage> _httpRequestPolicy;拿到響應中狀態碼,若為500則重試三次。
_httpRequestPolicy = Policy.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.InternalServerError).WaitAndRetryAsync(3,retryAttempt => TimeSpan.FromSeconds(retryAttempt));上述獲取請求響應策略在構造函數中獲取。
public class PollyController : ApiController{ ? ? ?? ? ??public readonly RetryPolicy<HttpResponseMessage> _httpRequestPolicy; ? ? ?
? ? ? ?public PollyController(){_httpRequestPolicy = Policy.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.InternalServerError).WaitAndRetryAsync(3,retryAttempt => TimeSpan.FromSeconds(retryAttempt));}}
此時調用接口時執行策略的Execute或者ExecuteAsync方法即可。
public async Task<IHttpActionResult> Get(){ ? ? ? ? ???var httpClient = new HttpClient(); ? ? ? ?
?? ?string requestEndpoint = "http://localhost:4096";HttpResponseMessage httpResponse = await _httpRequestPolicy.ExecuteAsync(() => httpClient.GetAsync(requestEndpoint));IEnumerable<string> numbers = await httpResponse.Content.ReadAsAsync<IEnumerable<string>>(); ? ? ? ? ? ?return Ok(numbers);}
你以為除了在Web APi中使用,在其他框架中也可以使用,例如EntityFramework 6.x中,在EntityFramework 6+上出現了執行策略,也就是執行重試機制,這個時候我們依然可以借助Polly輪子來實現。
EntityFramework 6.x使用Polly重試機制
在EntityFramework 6.x中有如下執行策略接口,看起來是不是和Polly中的Execute方法是不是很類似。
//
? ? // 摘要:
? ? //? ? ?A strategy that is used to execute a command or query against the database, possibly
? ? //? ? ?with logic to retry when a failure occurs.
? ? public interface IDbExecutionStrategy
? ? {
? ? ? ? //
? ? ? ? // 摘要:
? ? ? ? //? ? ?Indicates whether this System.Data.Entity.Infrastructure.IDbExecutionStrategy
? ? ? ? //? ? ?might retry the execution after a failure.
? ? ? ? bool RetriesOnFailure { get; }
? ? ? ? //
? ? ? ? // 摘要:
? ? ? ? //? ? ?Executes the specified operation.
? ? ? ? //
? ? ? ? // 參數:
? ? ? ? //? ?operation:
? ? ? ? //? ? ?A delegate representing an executable operation that doesn't return any results.
? ? ? ? void Execute(Action operation);
? ? ? ? //
? ? ? ? // 摘要:
? ? ? ? //? ? ?Executes the specified operation and returns the result.
? ? ? ? //
? ? ? ? // 參數:
? ? ? ? //? ?operation:
? ? ? ? //? ? ?A delegate representing an executable operation that returns the result of type
? ? ? ? //? ? ?TResult.
? ? ? ? //
? ? ? ? // 類型參數:
? ? ? ? //? ?TResult:
? ? ? ? //? ? ?The return type of operation.
? ? ? ? //
? ? ? ? // 返回結果:
? ? ? ? //? ? ?The result from the operation.
? ? ? ? TResult Execute<TResult>(Func<TResult> operation);
? ? ? ? //
? ? ? ? // 摘要:
? ? ? ? //? ? ?Executes the specified asynchronous operation.
? ? ? ? //
? ? ? ? // 參數:
? ? ? ? //? ?operation:
? ? ? ? //? ? ?A function that returns a started task.
? ? ? ? //
? ? ? ? //? ?cancellationToken:
? ? ? ? //? ? ?A cancellation token used to cancel the retry operation, but not operations that
? ? ? ? //? ? ?are already in flight or that already completed successfully.
? ? ? ? //
? ? ? ? // 返回結果:
? ? ? ? //? ? ?A task that will run to completion if the original task completes successfully
? ? ? ? //? ? ?(either the first time or after retrying transient failures). If the task fails
? ? ? ? //? ? ?with a non-transient error or the retry limit is reached, the returned task will
? ? ? ? //? ? ?become faulted and the exception must be observed.
? ? ? ? Task ExecuteAsync(Func<Task> operation, CancellationToken cancellationToken);
? ? ? ? //
? ? ? ? // 摘要:
? ? ? ? //? ? ?Executes the specified asynchronous operation and returns the result.
? ? ? ? //
? ? ? ? // 參數:
? ? ? ? //? ?operation:
? ? ? ? //? ? ?A function that returns a started task of type TResult.
? ? ? ? //
? ? ? ? //? ?cancellationToken:
? ? ? ? //? ? ?A cancellation token used to cancel the retry operation, but not operations that
? ? ? ? //? ? ?are already in flight or that already completed successfully.
? ? ? ? //
? ? ? ? // 類型參數:
? ? ? ? //? ?TResult:
? ? ? ? //? ? ?The result type of the System.Threading.Tasks.Task`1 returned by operation.
? ? ? ? //
? ? ? ? // 返回結果:
? ? ? ? //? ? ?A task that will run to completion if the original task completes successfully
? ? ? ? //? ? ?(either the first time or after retrying transient failures). If the task fails
? ? ? ? //? ? ?with a non-transient error or the retry limit is reached, the returned task will
? ? ? ? //? ? ?become faulted and the exception must be observed.
? ? ? ? [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
? ? ? ? Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> operation, CancellationToken cancellationToken);
? ? }
EntityFramework 6.x中的執行策略說到底就是數據庫連接問題即彈性連接,若考慮到數據庫過渡負載問題,此時應用程序和數據庫之間存在網絡問題的話。可能數據庫連接在幾秒內才返回,此時也沒有什么很大的問題,我們完全可以再嘗試一次,此時或許過了連接頻繁期,保證連接立馬恢復。如果數據庫連接一會恢復不了呢?或許是五分鐘,又或者是半個小時。如果我們只是一味盲目的進行重試,這顯然不可取。如果我們的應用程序連接超時時間超過了20秒,若我們選擇繼續連接到數據庫,我們將很快用完我們應用程序池中的工作線程。一直等待數據庫的響應。此時網站將完全無響應,同時會給用戶頁面無響應的友好提醒。這是Polly庫中描述斷路器的很好例子,換句話說如果我們捕獲了m個數量的SqlExceptions,假設數據庫有其他問題存在,導致我們不能在n秒內再嘗試連接數據庫。此時在數據庫連接上存在一個問題,那就是阻塞了我們的應用程序工作線程被掛起,我們試圖連接數據庫,我們假設不可用的話,但是我們要打破這種不可用,那就用Polly吧。
?
我們看到上述EntityFramework 6.x實現了IDbExecutionStrategy接口,但沒有實現如Polly中的斷路器模式,EntityFramework 6.x中的執行策略只是重試機制而已。 比如SqlAzureExecutionStrategy將在指定的時間段內重試指定的次數,直到一段時間段過去,重試指數過后,接著就是失敗。 同時所有后續調用將執行相同操作,重試并失敗。 這是調用數據庫時最好的策略嗎? 不敢肯定,或許Polly中的斷路器模式值得我們借鑒。我們自己來實現上述執行策略接口。
public class CirtuitBreakerExecutionStrategy : IDbExecutionStrategy{ ? ? ?? ? ?private Policy _policy; ? ?
? ? ?public CirtuitBreakerExecutionStrategy(Policy policy){_policy = policy;} ? ?
? ?? ?public void Execute(Action operation){_policy.Execute(() =>{operation.Invoke();});} ? ? ?
public TResult Execute<TResult>(Func<TResult> operation){ ? ? ? ? ? ?return _policy.Execute(() =>{ ? ? ? ? ? ? ? ?return operation.Invoke();});} ? ? ?
?public async Task ExecuteAsync(Func<Task> operation, CancellationToken cancellationToken){ ? ? ? ? ?
?await _policy.ExecuteAsync(() =>{ ? ? ?
return operation.Invoke();});} ? ? ? ?
public async Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> operation, CancellationToken cancellationToken){ ? ? ? ? ?
?return await _policy.ExecuteAsync(() =>{ ? ? ? ? ? ?
return operation.Invoke();});} ? ? ?
??public bool RetriesOnFailure { get { return true; } }}
接下來在基于代碼配置文件中設置我們上述自定義實現的斷路器模式。
? ?public class EFConfiguration : DbConfiguration{ ? ?? ? ? ?public Policy _policy; ? ?
? ?
? ? ? ?public EFConfiguration(){_policy = Policy.Handle<Exception>().CircuitBreaker(3, TimeSpan.FromSeconds(60));SetExecutionStrategy("System.Data.SqlClient", () => new CirtuitBreakerExecutionStrategy(_policy));}}
上述自定義實現執行策略不保證一定有用或許也是一種解決方案呢。
總結
本節我們介紹了強大的Polly庫和其對應使用的兩種實際場景,有此輪子我們何不用起,將其進行封裝可以用于一切重試、緩存、異常處理。?
相關文章:?
云計算設計模式(一)緩存預留模式
使用熔斷器設計模式保護軟件
云計算設計模式(二)——斷路器模式
API網關Ocelot 使用Polly 處理部分失敗問題
原文地址:http://www.cnblogs.com/CreateMyself/p/7589397.html
.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注
總結
以上是生活随笔為你收集整理的弹性和瞬态故障处理库Polly介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为什么你需要将代码迁移到ASP.NET
- 下一篇: SQL Server 2017 正式发布