Polly 是一套 .NET 處理瞬間故障的函式庫,提供開發人員用 Fluent API 方式及 Thread Safe 處理服務瞬間故障的策略,並提供重試(Retry)、斷路(Circuit-breaker)、超時(Timeout)、隔離(Bulkhead Isolation)、緩存(Cache)、回退(Fallback) 等機制,在上一篇 [NETCore] 使用 Polly 實現重試 (Retry) 策略 分享了 Retry 機制,這一篇就來介紹 Polly 中的 Timeout 策略,若有問題歡迎一起討論。
Polly 安裝與基本語法不再重複介紹,如果有興趣可以參考上一篇文章 : 傳送門。超時在開發中很常遇到,一般在發送 Request 到其他服務或是第三方伺服器時,都會設定 timeout 時間以避免使用者等待過久,過去在使用 httpClient 與 httpWebRequest 中可以透過 timeout 來調整等待設定,在 Polly 中設定 timeout 方式也是使用 Policy.Timeout API 來設定逾時時間,簡單範例如下
// 設定逾時時間為 10 秒 Policy.Timeout(10); // async Policy.TimeoutAsync(10);Polly 在 Timeout API 也提供多載方法方便開發者設定,同樣也可以使用 Action 方法
Policy.Timeout(10, (context, timespan, task) => { // do something , ex : log.info });
static void Main(string[] args) { Policy .Timeout(TimeSpan.FromMilliseconds(1), onTimeout: (context, timespan, task) => { Console.WriteLine($"{context.PolicyKey} : execution timed out after {timespan} seconds."); }) .Execute(doTimeOutHTTPRequest); } static string doTimeOutHTTPRequest() { Console.WriteLine($"開始發送 Request"); HttpResponseMessage response; using (HttpClient client = new HttpClient()) { client.Timeout = TimeSpan.FromMilliseconds(3); response = client.GetAsync(" http://www.mocky.io/v2/5cfed9a23200004f0045f284").Result; } return response.Content.ReadAsStringAsync().Result; }執行後顯示內容如下
如果開啟 debug mode 可以看到當 1ms 沒有完成任務,會觸發 TimeoutRejectedException 異常
另外,在 Timeout 策略中可以設定 TimeoutStrategy,分為 Optimistic 與 Pessimistic 兩種設定值, 預設為 Optimistic 也就是支援發送取消 (CancellationToken),TimeoutStrategy.Optimistic 中設定 CancellationToken 代碼如下
Policy timeoutPolicy = Policy.TimeoutAsync(30, TimeoutStrategy.Optimistic); HttpResponseMessage httpResponse = await timeoutPolicy .ExecuteAsync( async ct => await httpClient.GetAsync(requestEndpoint, ct), CancellationToken.None );如果對於 TimeoutStrategy 想解瞭更多的話,可以到官方 GitHub Timeout 文章中有針對 Optimistic Timeout 與 Pessimistic Timeout 使用上更詳細的說明與範例代碼 : 傳送門
Wrap 策略
Polly 支援多個故障時的策略機制,可以因應需求來組合其故障處理策略,這也是 Polly 彈性的設計,wrap 概念就像是策略包,可以將不同的故障策略包在一起設定為遇到故障時的處理機制方法,在組合時以下語法都支援
// Execute fallback.Execute(() => waitAndRetry.Execute(() => breaker.Execute(action))); // Policy.Wrap Policy .Handle<SomeExceptionType>() .Retry(7) // equivalently sample 1 fallback.Wrap(waitAndRetry.Wrap(breaker)).Execute(action); // equivalently sample 2 fallback.Wrap(waitAndRetry).Wrap(breaker).Execute(action);廢話不多說直接舉範例來說明,上一篇介紹了 Retry 策略這篇介紹了 Timeout 策略,我們就來結合兩個做為一個策略包,並在每個策略執行時將內容輸出在 console 上面
static async Task Main(string[] args) { var timeoutPolicys = Policy .Timeout(TimeSpan.FromMilliseconds(1), onTimeout: (context, timespan, task) => { Console.WriteLine($"{context.PolicyKey} : execution timed out after {timespan} seconds."); }); RetryPolicy waitAndRetryPolicy = Policy .Handle<Exception>() .Retry(3, onRetry: (exception, retryCount) => { Console.WriteLine($"[Polly retry] : 呼叫 API 異常, 進行第 {retryCount} 次重試"); }); FallbackPolicy<String> fallbackForTimeout = Policy<String> .Handle<TimeoutRejectedException>() .Fallback( fallbackValue: "Please try again later [Fallback for timeout]", onFallback: b => { Console.WriteLine($"這個請求超時了耶"); } ); FallbackPolicy<String> fallbackForAnyException = Policy<String> .Handle<Exception>() .Fallback( fallbackAction: () => { return "Please try again later [Fallback for any exception]"; }, onFallback: e => { Console.WriteLine($"[Polly fallback] : 重試失敗, say goodbye"); } ); PolicyWrap<String> policyWrap = fallbackForAnyException.Wrap(fallbackForTimeout).Wrap(waitAndRetryPolicy) .Wrap(timeoutPolicys); policyWrap.Execute(() => doMockHTTPRequest()); } static string doMockHTTPRequest() { Console.WriteLine($"開始發送 Request"); HttpResponseMessage result; using (HttpClient client = new HttpClient()) { client.Timeout = TimeSpan.FromMilliseconds(3); result = client.GetAsync("http://www.mocky.io/v2/5cfb4d9b3000006e080a8b0a").Result; } return result.Content.ReadAsStringAsync().Result; }執行之後輸出如下
同樣的在官網中也有提供 wrap 的範例說明 : 傳送門。
感想
在嘗試 wrap 的過程中花了不少時間跟踩雷,Handle 定義若是指定錯誤就會不會進到指定的故障策略中,舉例來說當 Timeout 時丟出的錯誤訊息為TimeoutRejectedException,若是沒有指定到此 exception 就不會進到 retry 機制當中,或是放大絕招直接handle exeption,在 Polly 的 wrap 中使用上還是有不少眉眉角角的細節要了解,也謝謝強者同事吉米斯提醒了重要的觀念才明白,日後若有比較值得分享的應用也會在PO出來,謝謝
參考
App-vNext/Polly
在嘗試 wrap 的過程中花了不少時間跟踩雷,Handle 定義若是指定錯誤就會不會進到指定的故障策略中,舉例來說當 Timeout 時丟出的錯誤訊息為TimeoutRejectedException,若是沒有指定到此 exception 就不會進到 retry 機制當中,或是放大絕招直接handle exeption,在 Polly 的 wrap 中使用上還是有不少眉眉角角的細節要了解,也謝謝強者同事吉米斯提醒了重要的觀念才明白,日後若有比較值得分享的應用也會在PO出來,謝謝
參考
App-vNext/Polly
0 意見:
張貼留言