Polly極簡介紹
Polly是一個被.NET基金會認可的彈性和瞬態故障處理庫,允許我們以非常順暢和線程安全的方式來執諸如行重試,斷路,超時,故障恢復等策略,其主要功能如下:
重試(Retry)
斷路器(Circuit-Breaker)
超時檢測(Timeout)
緩存(Cache)
降級(Fallback)
Polly的策略主要由“故障”和“動作”兩個部分組成,“故障”可以包括異常、超時等情況,“動作”則包括Fallback(降級)、重試(Retry)、熔斷(Circuit-Breaker)等。策略則用來執行業務代碼,當業務代碼出現了“故障”中的情況時就開始執行“動作”。
Polly 錯誤處理使用三步曲
- 定義條件: 定義你要處理的錯誤異常或返回結果
- 定義處理方式 : 重試、熔斷、回退
- 執行
定義條件
- 定義異常錯誤的條件
// 單個異常類型 Policy.Handle<Exception>(); // 限定條件的單個異常 Policy.Handle<Exception>(ex => ex.Message == "請求超時"); // 多個異常類型 Policy.Handle<Exception>().Or<ArgumentException>(); // 限定條件的多個異常 Policy.Handle<Exception>(ex => ex.Message == "請求超時") .Or<ArgumentException>(ex => ex.ParamName == "ID"); // Inner Exception 異常里面的異常類型 Policy.HandleInner<Exception>() .OrInner<ArgumentException>(ex => ex.ParamName == "ID");
- 定義返回結果的條件
// 返回結果加限定條件 Policy.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.NotFound); // 處理多個返回結果 Policy.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.InternalServerError) .OrResult(r => r.StatusCode == HttpStatusCode.BadGateway); // 處理元類型結果 (用.Equals) Policy.HandleResult(HttpStatusCode.InternalServerError) .OrResult(HttpStatusCode.BadGateway);
定義處理方式
重試
重試很好理解,當發生某種錯誤或者返回某種結果的時候進行重試
- 按次數重試
// 重試1次 Policy.Handle<Exception>().Retry(); // 重試3次 Policy.Handle<Exception>().Retry(3); // 重試3次,加上重試時的action參數 Policy.Handle<Exception>().Retry(3, (exception, retryCount) => { // do something });
- 不斷重試(直到成功)
// 不斷重試,直到成功 Policy.Handle<Exception>().RetryForever(); // 不斷重試,帶action參數在每次重試的時候執行 Policy.Handle<Exception>().RetryForever(exception => { // do something });
- 等待之后按次數重試
// 重試3次,每次等待5s Policy.Handle<Exception>().WaitAndRetry( 3, retryAttempt => TimeSpan.FromSeconds(5), // 處理異常、等待時間、重試第幾次 (exception, timespan, retryCount, context) => { // do something }); // 重試3次,分別等待1、2、3秒。 Policy.Handle<Exception>().WaitAndRetry(new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(3) });
熔斷
熔斷也可以被作為當遇到某種錯誤場景下的一個操作。
打開(Open)——熔斷器打開狀態,此時對目標服務的調用都直接返回錯誤,熔斷周期內不會走網絡請求,當熔斷周期結束時進入半開狀態;
關閉(Closed)——關閉狀態下正常發生網絡請求,但會記錄符合熔斷條件的連續執行次數,如果錯誤數量達到設定的閾值(如果在沒有達到閾值之前恢復正常,之前的累積次數將會歸零),熔斷狀態進入到打開狀態;
半開(Half-Open)——半開狀態下允許定量的服務請求,如果調用都成功則認為恢復了,關閉熔斷器,否則認為還沒好,又回到熔斷器打開狀態;
Policy.Handle<Exception>() .CircuitBreaker( // 熔斷前允許出現幾次錯誤 3, // 熔斷時間 TimeSpan.FromSeconds(5), // 熔斷時觸發 onBreak: (ex, breakDelay) => { Console.WriteLine("斷路器:開啟狀態(熔斷時觸發)"); }, // 熔斷恢復時觸發 onReset: () => { Console.WriteLine("斷路器:關閉狀態(熔斷恢復時觸發)"); }, // 熔斷時間到了之后觸發,嘗試放行少量(1次)的請求, onHalfOpen: () => { Console.WriteLine("斷路器:半開啟狀態(熔斷時間到了之后觸發)"); } );
超時檢測(Timeout)
Polly中關於超時的兩個策略:一個是悲觀策略(Pessimistic),一個是樂觀策略(Optimistic)。其中,悲觀策略超時后會直接拋異常,而樂觀策略則不會,而只是觸發CancellationTokenSource.Cancel函數,需要等待委托自行終止操作。一般情況下,我們都會用悲觀策略。
// 設置超時時間為30s Policy.Timeout(30, onTimeout: (context, timespan, task, ex) => { // do something }); // 超時分為樂觀超時與悲觀超時,樂觀超時依賴於CancellationToken ,它假設我們的具體執行的任務都支持CancellationToken。 // 那么在進行timeout的時候,它會通知執行線程取消並終止執行線程,避免額外的開銷。下面的樂觀超時的具體用法 。 HttpResponseMessage httpResponse = await Policy.TimeoutAsync(30) .ExecuteAsync( async ct => await new HttpClient().GetAsync(""), CancellationToken.None ); // 悲觀超時與樂觀超時的區別在於,如果執行的代碼不支持取消CancellationToken, // 它還會繼續執行,這會是一個比較大的開銷。 Policy.Timeout(30, TimeoutStrategy.Pessimistic);
艙壁
它用來限制某一個操作的最大並發執行數量
Policy.Bulkhead(12); // 同時,我們還可以控制一個等待處理的隊列長度 Policy.Bulkhead(12, 2); // 以及當請求執行操作被拒絕的時候,執行回調 Policy.Bulkhead(12, context => { // do something });
回退(Fallback)
降級的目的就是當某個服務提供者發生故障的時候,向調用方返回一個替代響應或者錯誤響應。
Policy.Handle<Exception>().Fallback(() => { // do something });
組合Polly
使用Wrap方法,將多個policy組合起來,其中策略的優先級是右到左。
var policyWrap = Policy.Wrap(fallback, breaker, timeout, retry, bulkhead); policyWrap.Execute(() => { // do something });
Polly測試項目
- 添加一個控制台項目,Nuget添加Polly引用
- 添加創建組合polly代碼
public static ISyncPolicy CreatePolly() { // 超時1秒 var timeoutPolicy = Policy.Timeout(1, TimeoutStrategy.Pessimistic, (context, timespan, task) => { Console.WriteLine("執行超時,拋出TimeoutRejectedException異常"); }); // 重試2次 var retryPolicy = Policy.Handle<Exception>() .WaitAndRetry( 2, retryAttempt => TimeSpan.FromSeconds(2), (exception, timespan, retryCount, context) => { Console.WriteLine($"{DateTime.Now} - 重試 {retryCount} 次 - 拋出{exception.GetType()}-{timespan.TotalMilliseconds}"); }); // 連續發生兩次故障,就熔斷3秒 var circuitBreakerPolicy = Policy.Handle<Exception>() .CircuitBreaker( // 熔斷前允許出現幾次錯誤 2, // 熔斷時間 TimeSpan.FromSeconds(5), // 熔斷時觸發 OPEN onBreak: (ex, breakDelay) => { Console.WriteLine($"{DateTime.Now} - 斷路器:開啟狀態(熔斷時觸發)"); }, // 熔斷恢復時觸發 // CLOSE onReset: () => { Console.WriteLine($"{DateTime.Now} - 斷路器:關閉狀態(熔斷恢復時觸發)"); }, // 熔斷時間到了之后觸發,嘗試放行少量(1次)的請求, onHalfOpen: () => { Console.WriteLine($"{DateTime.Now} - 斷路器:半開啟狀態(熔斷時間到了之后觸發)"); } ); // 回退策略,降級! var fallbackPolicy = Policy.Handle<Exception>() .Fallback(() => { Console.WriteLine("這是一個Fallback"); }, exception => { Console.WriteLine($"Fallback異常:{exception.GetType()}"); }); // 策略從右到左依次進行調用 // 首先判斷調用是否超時,如果超時就會觸發異常,發生超時故障,然后就觸發重試策略; // 如果重試兩次中只要成功一次,就直接返回調用結果 // 如果重試兩次都失敗,第三次再次失敗,就會發生故障 // 重試之后是斷路器策略,所以這個故障會被斷路器接收,當斷路器收到兩次故障,就會觸發熔斷,也就是說斷路器開啟 // 斷路器開啟的3秒內,任何故障或者操作,都會通過斷路器到達回退策略,觸發降級操作 // 3秒后,斷路器進入到半開啟狀態,操作可以正常執行 return Policy.Wrap(fallbackPolicy, circuitBreakerPolicy, retryPolicy, timeoutPolicy); }
- 添加測試代碼
var policy = CreatePolly(); for (int i = 0; i < 100; i++) { Console.WriteLine($"-------------第{i}次請求-------------"); policy.Execute(() => { // 從10次開始,正常請求成功 if (i < 10) { Thread.Sleep(3000); } else { Console.WriteLine($"{DateTime.Now}:請求成功"); } }); Thread.Sleep(1000); }
測試效果如下圖:
版權聲明:本文為CSDN博主「你的眼睛能看多遠了」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/u011210017/article/details/104213454