.Net Core 商城微服務專案系列(五):使用Polly處理服務錯誤
專案進行微服務化之後,隨之而來的問題就是服務呼叫過程中發生錯誤、超時等問題的時候我們該怎麼處理,比如因為網路的瞬時問題導致服務超時,這在我本人所在公司的專案裡是很常見的問題,當發生請求超時問題的時候,我們希望能夠自動重試,或者是在發生服務錯誤時採取一定的策略,比如限流熔斷等等。
本篇將會使用Polly處理服務呼叫過程中發生的超時問題。
開啟我們的MI.Web專案,通過NuGet引用 Microsoft.Extensions.Http 和 Microsoft.Extensions.Http.Polly。
在Startup中新增如下程式碼:
public static classServiceCollectionExtensions { public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration) {
//依賴注入 services.AddSingleton<IApiHelperService, ApiHelperService>(); services.AddSingleton<IAccountService, AccountService>(); services.AddSingleton<IPictureService, PictureService>(); services.AddOptions(); services.AddMvc(options => { options.Filters.Add<HttpGlobalExceptionFilter>(); }); services.AddMemoryCache(); services.AddCors(options=> { options.AddPolicy("CorsPolicy", builder => builder.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials()); }); return services; } public static IServiceCollection AddHttpServices(this IServiceCollection services) {//註冊http服務 //services.AddHttpClient(); services.AddHttpClient("MI") .AddPolicyHandler(GetRetryPolicy()) .AddPolicyHandler(GetCircuiBreakerPolicy()); return services; } /// <summary> /// 重試策略 /// </summary> public static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy() { return HttpPolicyExtensions .HandleTransientHttpError() .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound) .WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); } /// <summary> /// 熔斷策略 /// </summary> private static IAsyncPolicy<HttpResponseMessage> GetCircuiBreakerPolicy() { return HttpPolicyExtensions .HandleTransientHttpError() .CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)); } }
這裡我們通過Polly分別配置了重試和熔斷的策略,當發生404、500、408(超時)問題的時候會重試6次,間隔時間2秒;熔斷策略是如果有5個請求發生500或者超時則開啟熔斷,時間是30秒,Polly可以配置非常詳細的策略,以後有時間再專門介紹(其實是我現在不會。。。對不起)。因為這裡Polly是結合HttpClientFactory來使用的,所以我們需要使用上面的程式碼:
services.AddHttpClient("MI") .AddPolicyHandler(GetRetryPolicy()) .AddPolicyHandler(GetCircuiBreakerPolicy());
這裡可以理解為我們建立了一個名稱為“MI”的HttpClientFactory,然後為其配置了重試和熔斷策略,這裡順帶提一句是,HttpClientFactory是在.net core 2.1中加入的,它解決了之前HttpClient的資源釋放不及時的痛點,之前使用HttpClient時我們需要使用using或者建立靜態變數,前者的問題是頻繁的建立和銷燬帶來的資源損耗,不僅僅和物件資源,因為HttpClient還涉及到網路資源,後者則會導致資源釋放不及時,靜態資源如果不進行處理會一直存在,而HttpClientFactory內部會快取連線資源,同時會在不使用後的一段間隔時間後進行銷燬,同時記憶體會維護一個佇列,單例。
新增完上面這些後我們還需要在ConfigureServices方法中進行註冊:
public void ConfigureServices(IServiceCollection services) { services.AddCustomMvc(Configuration).AddHttpServices(); }
我為API的呼叫封裝了一個介面層:
public interface IApiHelperService { Task<T> PostAsync<T>(string url, IRequest request); Task<T> GetAsync<T>(string url); }
public class ApiHelperService : IApiHelperService { private readonly IHttpClientFactory _httpClientFactory; private readonly IMemoryCache cache; private readonly ILogger<ApiHelperService> _logger; public ApiHelperService(IMemoryCache cache, ILogger<ApiHelperService> _logger, IHttpClientFactory _httpClientFactory) { this._httpClientFactory = _httpClientFactory; this.cache = cache; this._logger = _logger; } /// <summary> /// HttpClient實現Post請求 /// </summary> public async Task<T> PostAsync<T>(string url, IRequest request) { var http = _httpClientFactory.CreateClient("MI"); //新增Token var token = await GetToken(); http.SetBearerToken(token); //使用FormUrlEncodedContent做HttpContent var httpContent = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json"); //await非同步等待迴應 var response = await http.PostAsync(url, httpContent); //確保HTTP成功狀態值 response.EnsureSuccessStatusCode(); //await非同步讀取 string Result = await response.Content.ReadAsStringAsync(); var Item = JsonConvert.DeserializeObject<T>(Result); return Item; } }
圖中標紅的部分就是使用帶有Polly策略的IHttpClientFactory來建立HttpClient,然後進行Post呼叫,Get呼叫也是同樣的。
然後我們啟動Web專案,開啟控制檯模式進行日誌檢視,訪問登入功能:
我們可以看到,一共訪問了登入方法兩次,第一次發生了404錯誤,接著自動又請求了一次,成功。
這裡只是做一次演示,接下來會在Ocelot閘道器中接入Polly,這樣可以避免在每個專案裡都進行這樣的配置,當然如果專案裡有功能需要進行特許的策略配置,是可以採用這種方式的。