Make resilient policies configurable.

This commit is contained in:
dsanz 2017-03-31 14:20:03 +02:00
parent 6ac5a8cc70
commit e9bdeb6f13
6 changed files with 129 additions and 23 deletions

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience.Policies
{
internal class CircuitBreakerPolicy : ResilientPolicy
{
public CircuitBreakerPolicy(int exceptionsAllowedBeforeBreaking, int durationOfBreakInMinutes)
{
ExceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
DurationOfBreakInMinutes = durationOfBreakInMinutes;
}
public int ExceptionsAllowedBeforeBreaking { get; }
public int DurationOfBreakInMinutes { get; }
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience.Policies
{
internal class RetryPolicy : ResilientPolicy
{
public RetryPolicy(int retries, int backoffSeconds, bool exponentialBackoff)
{
Retries = retries;
BackoffSeconds = backoffSeconds;
ExponentialBackoff = exponentialBackoff;
}
public int Retries { get; }
public int BackoffSeconds { get; }
public bool ExponentialBackoff { get; }
}
}

View File

@ -1,8 +1,10 @@
using Microsoft.Extensions.Logging; using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience.Policies;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
using Polly; using Polly;
using Polly.Wrap; using Polly.Wrap;
using System; using System;
using System.Collections.Generic;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -19,26 +21,58 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience
private PolicyWrap _policyWrapper; private PolicyWrap _policyWrapper;
private ILogger<ResilientHttpClient> _logger; private ILogger<ResilientHttpClient> _logger;
public HttpClient Inst => _client; public HttpClient Inst => _client;
public ResilientHttpClient(ILogger<ResilientHttpClient> logger)
public ResilientHttpClient(List<Policy> policies, ILogger<ResilientHttpClient> logger)
{ {
_client = new HttpClient(); _client = new HttpClient();
_logger = logger; _logger = logger;
// Add Policies to be applied // Add Policies to be applied
_policyWrapper = Policy.WrapAsync( _policyWrapper = Policy.WrapAsync(policies.ToArray());
CreateRetryPolicy(),
CreateCircuitBreakerPolicy()
);
} }
private Policy CreateRetryPolicy() => public ResilientHttpClient(List<ResilientPolicy> policies, ILogger<ResilientHttpClient> logger)
{
_client = new HttpClient();
_logger = logger;
// Add Policies to be applied
_policyWrapper = Policy.WrapAsync(GeneratePolicies(policies));
}
private Policy[] GeneratePolicies(IList<ResilientPolicy> policies)
{
var pollyPolicies = new List<Policy>();
foreach (var policy in policies)
{
switch (policy)
{
case RetryPolicy retryPolicy:
pollyPolicies.Add(
CreateRetryPolicy(
retryPolicy.Retries,
retryPolicy.BackoffSeconds,
retryPolicy.ExponentialBackoff));
break;
case CircuitBreakerPolicy circuitPolicy:
pollyPolicies.Add(
CreateCircuitBreakerPolicy(
circuitPolicy.ExceptionsAllowedBeforeBreaking,
circuitPolicy.DurationOfBreakInMinutes));
break;
}
}
return pollyPolicies.ToArray();
}
private Policy CreateRetryPolicy(int retries, int backoffSeconds, bool exponentialBackoff) =>
Policy.Handle<HttpRequestException>() Policy.Handle<HttpRequestException>()
.WaitAndRetryAsync( .WaitAndRetryAsync(
// number of retries retries,
6, retryAttempt => exponentialBackoff ? TimeSpan.FromSeconds(Math.Pow(backoffSeconds, retryAttempt)) : TimeSpan.FromSeconds(backoffSeconds),
// exponential backofff
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
// on retry
(exception, timeSpan, retryCount, context) => (exception, timeSpan, retryCount, context) =>
{ {
var msg = $"Retry {retryCount} implemented with Polly's RetryPolicy " + var msg = $"Retry {retryCount} implemented with Polly's RetryPolicy " +
@ -50,22 +84,20 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience
} }
); );
private Policy CreateCircuitBreakerPolicy() => private Policy CreateCircuitBreakerPolicy(int exceptionsAllowedBeforeBreaking, int durationOfBreakInMinutes) =>
Policy.Handle<HttpRequestException>() Policy.Handle<HttpRequestException>()
.CircuitBreakerAsync( .CircuitBreakerAsync(
// number of exceptions before breaking circuit exceptionsAllowedBeforeBreaking,
5, TimeSpan.FromMinutes(durationOfBreakInMinutes),
// time circuit opened before retry
TimeSpan.FromMinutes(1),
(exception, duration) => (exception, duration) =>
{ {
// on circuit opened // on circuit opened
_logger.LogTrace("Circuit breaker opened"); _logger.LogTrace("Circuit breaker opened");
}, },
() => () =>
{ {
// on circuit closed // on circuit closed
_logger.LogTrace("Circuit breaker reset"); _logger.LogTrace("Circuit breaker reset");
} }
); );

View File

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience
{
public class ResilientPolicy
{
}
}

View File

@ -0,0 +1,20 @@
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience.Policies;
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience
{
public static class ResilientPolicyFactory
{
public static ResilientPolicy CreateRetryPolicy(int retries, int backoffSeconds, bool exponentialBackoff)
{
return new RetryPolicy(retries, backoffSeconds, exponentialBackoff);
}
public static ResilientPolicy CreateCiscuitBreakerPolicy(int exceptionsAllowedBeforeBreaking, int durationOfBreakInMinutes)
{
return new CircuitBreakerPolicy(exceptionsAllowedBeforeBreaking, durationOfBreakInMinutes);
}
}
}

View File

@ -58,8 +58,14 @@ namespace Microsoft.eShopOnContainers.WebMVC
services.AddTransient<IBasketService, BasketService>(); services.AddTransient<IBasketService, BasketService>();
services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>(); services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>();
if(Configuration.GetValue<string>("ActivateCircuitBreaker") == bool.TrueString) if (Configuration.GetValue<string>("ActivateCircuitBreaker") == bool.TrueString)
{ {
services.AddSingleton(
new List<ResilientPolicy>
{
ResilientPolicyFactory.CreateRetryPolicy(6, 2, true),
ResilientPolicyFactory.CreateCiscuitBreakerPolicy(5, 1)
});
services.AddTransient<IHttpClient, ResilientHttpClient>(); services.AddTransient<IHttpClient, ResilientHttpClient>();
} }
else else