1. 程式人生 > >.Net Core 商城微服務專案系列(五):使用Polly處理服務錯誤

.Net Core 商城微服務專案系列(五):使用Polly處理服務錯誤

專案進行微服務化之後,隨之而來的問題就是服務呼叫過程中發生錯誤、超時等問題的時候我們該怎麼處理,比如因為網路的瞬時問題導致服務超時,這在我本人所在公司的專案裡是很常見的問題,當發生請求超時問題的時候,我們希望能夠自動重試,或者是在發生服務錯誤時採取一定的策略,比如限流熔斷等等。

本篇將會使用Polly處理服務呼叫過程中發生的超時問題。

開啟我們的MI.Web專案,通過NuGet引用 Microsoft.Extensions.Http 和 Microsoft.Extensions.Http.Polly。

在Startup中新增如下程式碼:

    public static class
ServiceCollectionExtensions { 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,這樣可以避免在每個專案裡都進行這樣的配置,當然如果專案裡有功能需要進行特許的策略配置,是可以採用這種方式的。