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 意見:
張貼留言