Changes using fluent configuration API from Polly. Still to be cleaned up and old Policy code to be removed, etc.
This commit is contained in:
parent
18e6dc4afb
commit
dc5de83747
@ -1,5 +1,7 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Polly;
|
using Polly;
|
||||||
|
using Polly.CircuitBreaker;
|
||||||
|
using Polly.Retry;
|
||||||
using System;
|
using System;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
|
||||||
@ -7,8 +9,13 @@ namespace WebMVC.Infrastructure
|
|||||||
{
|
{
|
||||||
public class HttpClientDefaultPolicies
|
public class HttpClientDefaultPolicies
|
||||||
{
|
{
|
||||||
const int RETRY_COUNT = 6;
|
//Config for Retries with exponential backoff policy
|
||||||
const int EXCEPTIONS_ALLOWED_BEFORE_CIRCUIT_BREAKER = 5;
|
const int MAX_RETRIES = 6;
|
||||||
|
const int SECONDS_BASE_FOR_EXPONENTIAL_BACKOFF = 2;
|
||||||
|
|
||||||
|
//Config for Circuit Breaker policy
|
||||||
|
const int EXCEPTIONS_ALLOWED_BEFORE_CIRCUIT_BREAKES = 5;
|
||||||
|
const int DURATION_OF_BREAK_IN_MINUTES = 1;
|
||||||
|
|
||||||
private readonly ILogger<HttpClientDefaultPolicies> _logger;
|
private readonly ILogger<HttpClientDefaultPolicies> _logger;
|
||||||
|
|
||||||
@ -17,14 +24,15 @@ namespace WebMVC.Infrastructure
|
|||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Policy GetWaitAndRetryPolicy()
|
public IAsyncPolicy<HttpResponseMessage> GetWaitAndRetryPolicy()
|
||||||
{
|
{
|
||||||
return Policy.Handle<HttpRequestException>()
|
RetryPolicy retryPolicy =
|
||||||
|
Policy.Handle<HttpRequestException>()
|
||||||
.WaitAndRetryAsync(
|
.WaitAndRetryAsync(
|
||||||
// number of retries
|
// Maximum number of retries
|
||||||
RETRY_COUNT,
|
MAX_RETRIES,
|
||||||
// exponential backofff
|
// exponential backofff (2sg, 4sg, 8sg, 16sg, 32sg. etc.)
|
||||||
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
|
retryAttempt => TimeSpan.FromSeconds(Math.Pow(SECONDS_BASE_FOR_EXPONENTIAL_BACKOFF, retryAttempt)),
|
||||||
// on retry
|
// on retry
|
||||||
(exception, timeSpan, retryCount, context) =>
|
(exception, timeSpan, retryCount, context) =>
|
||||||
{
|
{
|
||||||
@ -35,26 +43,32 @@ namespace WebMVC.Infrastructure
|
|||||||
_logger.LogWarning(msg);
|
_logger.LogWarning(msg);
|
||||||
_logger.LogDebug(msg);
|
_logger.LogDebug(msg);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return retryPolicy.AsAsyncPolicy<HttpResponseMessage>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Policy GetCircuitBreakerPolicy()
|
public IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()
|
||||||
{
|
{
|
||||||
return Policy.Handle<HttpRequestException>()
|
CircuitBreakerPolicy circuitBreakerPolicy =
|
||||||
|
Policy.Handle<HttpRequestException>()
|
||||||
.CircuitBreakerAsync(
|
.CircuitBreakerAsync(
|
||||||
// number of exceptions before breaking circuit
|
// Number of exceptions before breaking the circuit
|
||||||
EXCEPTIONS_ALLOWED_BEFORE_CIRCUIT_BREAKER,
|
EXCEPTIONS_ALLOWED_BEFORE_CIRCUIT_BREAKES,
|
||||||
// time circuit opened before retry
|
// Duration of break
|
||||||
TimeSpan.FromMinutes(1),
|
TimeSpan.FromMinutes(DURATION_OF_BREAK_IN_MINUTES),
|
||||||
(exception, duration) =>
|
(exception, duration) =>
|
||||||
{
|
{
|
||||||
// on circuit opened
|
// On circuit opened (Circuit is broken)
|
||||||
_logger.LogTrace("Circuit breaker opened");
|
_logger.LogTrace("Circuit has been broken");
|
||||||
},
|
},
|
||||||
() =>
|
() =>
|
||||||
{
|
{
|
||||||
// on circuit closed
|
// on circuit closed
|
||||||
_logger.LogTrace("Circuit breaker reset");
|
_logger.LogTrace("Circuit has been reset");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return circuitBreakerPolicy.AsAsyncPolicy<HttpResponseMessage>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,11 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
using Microsoft.Extensions.HealthChecks;
|
using Microsoft.Extensions.HealthChecks;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Polly;
|
using Polly;
|
||||||
|
using Polly.CircuitBreaker;
|
||||||
|
using Polly.Extensions.Http;
|
||||||
using Polly.Registry;
|
using Polly.Registry;
|
||||||
|
using Polly.Retry;
|
||||||
|
using Polly.Timeout;
|
||||||
using StackExchange.Redis;
|
using StackExchange.Redis;
|
||||||
using System;
|
using System;
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
@ -35,13 +39,13 @@ namespace Microsoft.eShopOnContainers.WebMVC
|
|||||||
|
|
||||||
public IConfiguration Configuration { get; }
|
public IConfiguration Configuration { get; }
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to add services to the container.
|
// This method gets called by the runtime. Use this method to add services to the IoC container.
|
||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddAppInsight(Configuration)
|
services.AddAppInsight(Configuration)
|
||||||
.AddHealthChecks(Configuration)
|
.AddHealthChecks(Configuration)
|
||||||
.AddCustomMvc(Configuration)
|
.AddCustomMvc(Configuration)
|
||||||
.AddCustomApplicationServices(Configuration)
|
.AddHttpClientServices(Configuration)
|
||||||
.AddCustomAuthentication(Configuration);
|
.AddCustomAuthentication(Configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,28 +169,89 @@ namespace Microsoft.eShopOnContainers.WebMVC
|
|||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddCustomApplicationServices(this IServiceCollection services, IConfiguration configuration)
|
// Adds all Http client services (like Service-Agents) using resilient Http requests based on HttpClient factory and Polly's policies
|
||||||
|
public static IServiceCollection AddHttpClientServices(this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
|
// (CDLTLL) TEMPORAL COMMENT: Do we need this line of code if using HttpClient factory?
|
||||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||||
|
|
||||||
|
// (CDLTLL) I don't think we need this HttpClientDefaultPolicies if using fluent configuration ,as below...
|
||||||
|
// Create a singleton object with the by default policies
|
||||||
services.AddSingleton<HttpClientDefaultPolicies>();
|
services.AddSingleton<HttpClientDefaultPolicies>();
|
||||||
var defaultPolicies = services.BuildServiceProvider().GetService<HttpClientDefaultPolicies>();
|
var defaultPolicies = services.BuildServiceProvider().GetService<HttpClientDefaultPolicies>();
|
||||||
|
|
||||||
var registry = services.AddPolicyRegistry();
|
// Create a Polly-Policy-Registry with the by default policies for resilient Http requests
|
||||||
registry.Add("WaitAndRetry", defaultPolicies.GetWaitAndRetryPolicy());
|
var pollyPolicyRegistry = services.AddPolicyRegistry();
|
||||||
registry.Add("CircuitBreaker", defaultPolicies.GetCircuitBreakerPolicy());
|
|
||||||
|
|
||||||
|
|
||||||
|
//Using fluent client configuration of Polly policies
|
||||||
|
|
||||||
|
var retriesWithExponentialBackoff = HttpPolicyExtensions
|
||||||
|
.HandleTransientHttpError()
|
||||||
|
.WaitAndRetryAsync(7,
|
||||||
|
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))
|
||||||
|
);
|
||||||
|
//(CDLTLL) Instead of hardcoded values, use: configuration["HttpClientMaxNumberRetries"], configuration["SecondsBaseForExponentialBackoff"]
|
||||||
|
|
||||||
|
var circuitBreaker = HttpPolicyExtensions
|
||||||
|
.HandleTransientHttpError()
|
||||||
|
.CircuitBreakerAsync(6,
|
||||||
|
TimeSpan.FromSeconds(30)
|
||||||
|
);
|
||||||
|
//(CDLTLL) Instead of hardcoded values, use: configuration["HttpClientExceptionsAllowedBeforeBreaking"], configuration["DurationOfBreakInMinutes"]
|
||||||
|
|
||||||
|
|
||||||
|
pollyPolicyRegistry.Add("DefaultRetriesWithExponentialBackoff", retriesWithExponentialBackoff);
|
||||||
|
pollyPolicyRegistry.Add("DefaultCircuitBreaker", circuitBreaker);
|
||||||
|
|
||||||
|
|
||||||
|
// (CDLTLL) Using "OLD" policies. I don't like it much...
|
||||||
|
//pollyPolicyRegistry.Add("DefaultRetriesWithExponentialBackoff", defaultPolicies.GetWaitAndRetryPolicy()); // (CDLTLL) TEMPORAL COMMENT: Arguments here would be a good place to propagate the configuration values --> GetWaitAndRetryPolicy(configuration["HttpClientMaxNumberRetries"], configuration["SecondsBaseForExponentialBackoff"])
|
||||||
|
//pollyPolicyRegistry.Add("DefaultCircuitBreaker", defaultPolicies.GetCircuitBreakerPolicy()); // (CDLTLL) TEMPORAL COMMENT: Arguments here would be a good place to propagate the configuration values --> GetCircuitBreakerPolicy(configuration["HttpClientExceptionsAllowedBeforeBreaking"], configuration["DurationOfBreakInMinutes"])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// (CDLTLL) Handlers need to be transient. //(CDLTLL) TEMPORAL COMMENT: This registration was missing, hence the error "InvalidOperationException: No service for type 'WebMVC.Infrastructure.HttpClientAuthorizationDelegatingHandler' has been registered"
|
||||||
|
services.AddTransient<HttpClientAuthorizationDelegatingHandler>();
|
||||||
|
|
||||||
|
//Add all Typed-Clients (Service-Agents) through the HttpClient factory to implement Resilient Http requests
|
||||||
|
//
|
||||||
|
|
||||||
|
//Add BasketService typed client (Service Agent)
|
||||||
services.AddHttpClient<IBasketService, BasketService>()
|
services.AddHttpClient<IBasketService, BasketService>()
|
||||||
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
|
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>() //Additional Authentication-Delegating-Handler to add the OAuth-Bearer-token to the Http headers
|
||||||
.AddPolicyHandlerFromRegistry("WaitAndRetry")
|
.AddPolicyHandlerFromRegistry("DefaultRetriesWithExponentialBackoff")
|
||||||
.AddPolicyHandlerFromRegistry("CircuitBreaker");
|
.AddPolicyHandlerFromRegistry("DefaultCircuitBreaker");
|
||||||
|
|
||||||
|
//Add CatalogService typed client (Service Agent)
|
||||||
|
//services.AddHttpClient<ICatalogService, CatalogService>() ...
|
||||||
|
|
||||||
|
//Add OrderingService typed client (Service Agent)
|
||||||
|
//services.AddHttpClient<IOrderingService, OrderingService>() ...
|
||||||
|
|
||||||
|
//Add CampaignService typed client (Service Agent)
|
||||||
|
//services.AddHttpClient<ICampaignService, CampaignService>() ...
|
||||||
|
|
||||||
|
//Add LocationService typed client (Service Agent)
|
||||||
|
//services.AddHttpClient<ILocationService, LocationService>() ...
|
||||||
|
|
||||||
|
//Add IdentityParser typed client (Service Agent)
|
||||||
|
//services.AddHttpClient<IIdentityParser, IdentityParser>() ...
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// (CDLTLL) TEMPORAL COMMENT: The following Typed-Clients (Service-Agents) have to be coded as BasketService by using AddHttpClient<>(), right?
|
||||||
|
// This code will be deleted
|
||||||
services.AddTransient<ICatalogService, CatalogService>();
|
services.AddTransient<ICatalogService, CatalogService>();
|
||||||
services.AddTransient<IOrderingService, OrderingService>();
|
services.AddTransient<IOrderingService, OrderingService>();
|
||||||
|
|
||||||
services.AddTransient<ICampaignService, CampaignService>();
|
services.AddTransient<ICampaignService, CampaignService>();
|
||||||
services.AddTransient<ILocationService, LocationService>();
|
services.AddTransient<ILocationService, LocationService>();
|
||||||
services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>();
|
services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>();
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// TEMPORAL COMMENT: This code will be deleted when using HttpClientFactory, right?
|
||||||
if (configuration.GetValue<string>("UseResilientHttp") == bool.TrueString)
|
if (configuration.GetValue<string>("UseResilientHttp") == bool.TrueString)
|
||||||
{
|
{
|
||||||
services.AddSingleton<IResilientHttpClientFactory, ResilientHttpClientFactory>(sp =>
|
services.AddSingleton<IResilientHttpClientFactory, ResilientHttpClientFactory>(sp =>
|
||||||
@ -214,6 +279,7 @@ namespace Microsoft.eShopOnContainers.WebMVC
|
|||||||
{
|
{
|
||||||
services.AddSingleton<IHttpClient, StandardHttpClient>();
|
services.AddSingleton<IHttpClient, StandardHttpClient>();
|
||||||
}
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user