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 來設定逾時時間,簡單範例如下
Polly 在 Timeout API 也提供多載方法方便開發者設定,同樣也可以使用 Action 方法
- // 設定逾時時間為 10 秒
- Policy.Timeout(10);
- // async
- Policy.TimeoutAsync(10);
- 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 代碼如下
如果對於 TimeoutStrategy 想解瞭更多的話,可以到官方 GitHub Timeout 文章中有針對 Optimistic Timeout 與 Pessimistic Timeout 使用上更詳細的說明與範例代碼 : 傳送門
- Policy timeoutPolicy = Policy.TimeoutAsync(30, TimeoutStrategy.Optimistic);
- HttpResponseMessage httpResponse = await timeoutPolicy
- .ExecuteAsync(
- async ct => await httpClient.GetAsync(requestEndpoint, ct),
- CancellationToken.None
- );
Wrap 策略
Polly 支援多個故障時的策略機制,可以因應需求來組合其故障處理策略,這也是 Polly 彈性的設計,wrap 概念就像是策略包,可以將不同的故障策略包在一起設定為遇到故障時的處理機制方法,在組合時以下語法都支援
廢話不多說直接舉範例來說明,上一篇介紹了 Retry 策略這篇介紹了 Timeout 策略,我們就來結合兩個做為一個策略包,並在每個策略執行時將內容輸出在 console 上面
- // 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);
執行之後輸出如下
- 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 意見:
張貼留言