文章目錄
Polly retry 之後的行為是?
同事在追查 bug 時,問到在執行某個動作時使用的 Polly policy 來進行失敗重試,如果 policy 中設定的 retry 次數結束仍然失敗會發生什麼事? 印象中之前測試的結果會是直接拋出結果,但時間一久我也忘了XD 於是我就來做個實驗 加深印象吧 ~~
基本環境說明
- macOS Majave 10.14.16
- .NET Core SDK 3.0.100
- JetBrains Rider 2019.2.2
ASP.NET Core MVC 3.0 預設專案範本
Startup.cs
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddHttpClient("test",client => { client.BaseAddress = new Uri("http://test.yowko.com"); client.Timeout=TimeSpan.FromSeconds(5); }); }
Controller
[ApiController] [Route("[controller]/[action]")] public class TestHttpClientController : ControllerBase { private readonly HttpClient _httpClient; public TestHttpClientController(IHttpClientFactory httpClientFactory) { _httpClient = _httpClientFactory.CreateClient("test"); } }
NuGet packages
- Polly 7.1.1
- Microsoft.Extensions.Http.Polly 3.0.0
測試內容
測試用 url http://test.yowko.com 沒有實際提供服務
訂定 Polly policy
如果出現 Exception 或是 StatusCode 不是 Success 就進行 retry 3次,每次 retry 會紀錄 retry 時間,並且間隔 1.1 的 retry 次數次方秒
_pollyPolicy = Policy .Handle<Exception>() .OrResult<HttpResponseMessage>(a => !a.IsSuccessStatusCode) .WaitAndRetryAsync(3, retryAttempt => { Console.WriteLine($"polly retry@{DateTime.Now}"); return TimeSpan.FromSeconds(Math.Pow(1.1, retryAttempt)); });
實際使用
var result= await _pollyPolicy.ExecuteAsync(async () => await _httpClient.GetAsync("")); if (result.IsSuccessStatusCode) return await result.Content.ReadAsStringAsync(); return string.Empty;
實際結果
結果跟印象中一樣:最後一次執行會直接將結果拋出來
攔截可能的 Exception
在 Execute policy 中使用 Try Catch
var result = await _pollyPolicy.ExecuteAsync(async () => { try { return await _httpClient.GetAsync(""); } catch (Exception e) { Console.WriteLine($"Exception @ {DateTime.Now}:{e.Message}"); return new HttpResponseMessage(HttpStatusCode.InternalServerError); } }); if (result.IsSuccessStatusCode) return await result.Content.ReadAsStringAsync(); return string.Empty;
將整個 Execute policy 使用 Try Catch 包
try { var result = await _pollyPolicy.ExecuteAsync(async () => await _httpClient.GetAsync("")); if (result.IsSuccessStatusCode) return await result.Content.ReadAsStringAsync(); return string.Empty; } catch (Exception e) { Console.WriteLine($"Exception @ {DateTime.Now}:{e.Message}"); return string.Empty; }
使用
ExecuteAndCaptureAsync
方法var result= await _pollyPolicy.ExecuteAndCaptureAsync(async () => await _httpClient.GetAsync("")); if (result.FinalException==null) return await result.Result.Content.ReadAsStringAsync(); else Console.WriteLine($"Exception : {result.ExceptionType} @ {DateTime.Now}:{result.FinalException.Message}"); return string.Empty;
ExecuteAndCaptureAsync 的 result 有下列幾個屬性
- Outcome - 顯示執行結果
- 成功 :
Successful
- 失敗:
Failure
- 成功 :
- FinalException - 攔截最後發生的 Exception,如果最終結果是成功這個值會為 null
- ExceptionType - 最後發生 Exception 的類型,如果最終結果是成功這個值會為 null
Unhandled
:未被 policy 處理HandledByThisPolicy
: 被 policy 處理
- Result - 在執行 func 時,如果成功就回傳成功結果,否則就回傳型別的預值
- Outcome - 顯示執行結果
心得
三種方式可以依實際需要使用,我個人則是較常使用 ExecuteAndCaptureAsync
,不過來實際結果來看第二種方式 (將整個 Execute policy 使用 Try Catch 包) 與第三種方式(ExecuteAndCaptureAsync
) 感覺上差不多,而第一種方式(在 Execute policy 中使用 Try Catch)則比較適用想確實收到每次 exception 的細節,不過需要留意因為多了一層 try catch,在大量使用下的效能損耗
參考資訊
文章作者 Yowko Tsai
上次更新 2021-10-29
授權合約
本部落格 (Yowko's Notes) 所有的文章內容(包含圖片),任何轉載行為,必須通知並獲本部落格作者 (Yowko Tsai) 的同意始得轉載,且轉載皆須註明出處與作者。
Yowko's Notes 由 Yowko Tsai 製作,以創用CC 姓名標示-非商業性-相同方式分享 3.0 台灣 授權條款 釋出。