private static async Task <HttpResponseMessage> TryExecuteAsync(Func <Task <HttpResponseMessage> > action) { var retryErrorCode = new HttpStatusCode[] { HttpStatusCode.BadRequest, HttpStatusCode.Unauthorized, HttpStatusCode.Forbidden, HttpStatusCode.InternalServerError }; var response = await Policy // When recieving a ApiException with status code 401 or 500 .HandleResult <HttpResponseMessage>(message => retryErrorCode.Contains(message.StatusCode)) // Retry count but execute some code before retrying .RetryAsync(3) .ExecuteAsync(action); response.EnsureSuccessStatusCode(); return(response); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { #region Discount gRPC client // Enable support for unencrypted HTTP2 //AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); //AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2Support", true); Random jitterer = new Random(); var loggerFactory = LoggerFactory.Create(logging => { logging.AddConsole(); logging.SetMinimumLevel(LogLevel.Debug); }); var serverErrors = new HttpStatusCode[] { HttpStatusCode.BadGateway, HttpStatusCode.GatewayTimeout, HttpStatusCode.ServiceUnavailable, HttpStatusCode.InternalServerError, HttpStatusCode.TooManyRequests, HttpStatusCode.RequestTimeout }; var gRpcErrors = new StatusCode[] { StatusCode.DeadlineExceeded, StatusCode.Internal, StatusCode.NotFound, StatusCode.ResourceExhausted, StatusCode.Unavailable, StatusCode.Unknown }; Func <HttpRequestMessage, IAsyncPolicy <HttpResponseMessage> > retryFunc = (request) => { return(Policy.HandleResult <HttpResponseMessage>(r => { var grpcStatus = StatusManager.GetStatusCode(r); var httpStatusCode = r.StatusCode; return (grpcStatus == null && serverErrors.Contains(httpStatusCode)) || // if the server send an error before gRPC pipeline (httpStatusCode == HttpStatusCode.OK && gRpcErrors.Contains(grpcStatus.Value)); // if gRPC pipeline handled the request (gRPC always answers OK) }) .WaitAndRetryAsync(3, (input) => TimeSpan.FromSeconds(3 + input), (result, timeSpan, retryCount, context) => { var grpcStatus = StatusManager.GetStatusCode(result.Result); Console.WriteLine($"Request failed with {grpcStatus}. Retry"); })); }; services.AddGrpcClient <DiscountProtoService.DiscountProtoServiceClient>(options => { options.Address = new Uri(Configuration["GrpcSettings:DiscountUrl"]); options.ChannelOptionsActions.Add(channelOptions => { channelOptions.Credentials = ChannelCredentials.Insecure; }); }).AddPolicyHandler(retryFunc); services.AddScoped <DiscountGrpcService>(); #endregion #region Redis connection configuration services.AddStackExchangeRedisCache(options => { options.Configuration = Configuration.GetValue <string>("CacheSettings:ConnectionString"); }); #endregion services.AddControllers(); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "Basket.API", Version = "v1" }); }); #region Data repositories registration services.AddScoped <IBasketRepository, BasketRepository>(); #endregion }