项目进行微服务化以后,随之而来的问题就是服务调用过程当中发生错误、超时等问题的时候咱们该怎么处理,好比由于网络的瞬时问题致使服务超时,这在我本人所在公司的项目里是很常见的问题,当发生请求超时问题的时候,咱们但愿可以自动重试,或者是在发生服务错误时采起必定的策略,好比限流熔断等等。json
本篇将会使用Polly处理服务调用过程当中发生的超时问题。缓存
打开咱们的MI.Web项目,经过NuGet引用 Microsoft.Extensions.Http 和 Microsoft.Extensions.Http.Polly。网络
在Startup中添加以下代码:app
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分别配置了重试和熔断的策略,当发生40四、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内部会缓存链接资源,同时会在不使用后的一段间隔时间后进行销毁,同时内存会维护一个队列,单例。async
添加完上面这些后咱们还须要在ConfigureServices方法中进行注册:微服务
public void ConfigureServices(IServiceCollection services) { services.AddCustomMvc(Configuration).AddHttpServices(); }
我为API的调用封装了一个接口层:ui
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调用也是一样的。this
而后咱们启动Web项目,开启控制台模式进行日志查看,访问登陆功能:url
咱们能够看到,一共访问了登陆方法两次,第一次发生了404错误,接着自动又请求了一次,成功。
这里只是作一次演示,接下来会在Ocelot网关中接入Polly,这样能够避免在每一个项目里都进行这样的配置,固然若是项目里有功能须要进行特许的策略配置,是能够采用这种方式的。