Refactoring resilience implementation in order to use Polly directly when creating the resilience policies
This commit is contained in:
parent
e05cbfa809
commit
0c317d56f3
@ -1,18 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http.Policies
|
|
||||||
{
|
|
||||||
internal class CircuitBreakerPolicy : ResiliencePolicy
|
|
||||||
{
|
|
||||||
public CircuitBreakerPolicy(int exceptionsAllowedBeforeBreaking, int durationOfBreakInMinutes)
|
|
||||||
{
|
|
||||||
ExceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
|
|
||||||
DurationOfBreakInMinutes = durationOfBreakInMinutes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int ExceptionsAllowedBeforeBreaking { get; }
|
|
||||||
public int DurationOfBreakInMinutes { get; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http.Policies
|
|
||||||
{
|
|
||||||
internal class RetryPolicy : ResiliencePolicy
|
|
||||||
{
|
|
||||||
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; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http.Policies;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http
|
|
||||||
{
|
|
||||||
public static class ResiliencePolicyFactory
|
|
||||||
{
|
|
||||||
public static ResiliencePolicy CreateRetryPolicy(int retries, int backoffSeconds, bool exponentialBackoff)
|
|
||||||
{
|
|
||||||
return new RetryPolicy(retries, backoffSeconds, exponentialBackoff);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ResiliencePolicy CreateCiscuitBreakerPolicy(int exceptionsAllowedBeforeBreaking, int durationOfBreakInMinutes)
|
|
||||||
{
|
|
||||||
return new CircuitBreakerPolicy(exceptionsAllowedBeforeBreaking, durationOfBreakInMinutes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,4 @@
|
|||||||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http.Policies;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Polly;
|
using Polly;
|
||||||
using Polly.Wrap;
|
using Polly.Wrap;
|
||||||
@ -23,85 +22,15 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http
|
|||||||
private ILogger<ResilientHttpClient> _logger;
|
private ILogger<ResilientHttpClient> _logger;
|
||||||
public HttpClient Inst => _client;
|
public HttpClient Inst => _client;
|
||||||
|
|
||||||
public ResilientHttpClient(List<Policy> policies, ILogger<ResilientHttpClient> logger)
|
public ResilientHttpClient(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(policies.ToArray());
|
_policyWrapper = Policy.WrapAsync(policies);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResilientHttpClient(List<ResiliencePolicy> policies, ILogger<ResilientHttpClient> logger)
|
|
||||||
{
|
|
||||||
_client = new HttpClient();
|
|
||||||
_logger = logger;
|
|
||||||
|
|
||||||
// Add Policies to be applied
|
|
||||||
_policyWrapper = Policy.WrapAsync(GeneratePolicies(policies));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Policy[] GeneratePolicies(IList<ResiliencePolicy> 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>()
|
|
||||||
.WaitAndRetryAsync(
|
|
||||||
retries,
|
|
||||||
retryAttempt => exponentialBackoff ? TimeSpan.FromSeconds(Math.Pow(backoffSeconds, retryAttempt)) : TimeSpan.FromSeconds(backoffSeconds),
|
|
||||||
(exception, timeSpan, retryCount, context) =>
|
|
||||||
{
|
|
||||||
var msg = $"Retry {retryCount} implemented with Polly's RetryPolicy " +
|
|
||||||
$"of {context.PolicyKey} " +
|
|
||||||
$"at {context.ExecutionKey}, " +
|
|
||||||
$"due to: {exception}.";
|
|
||||||
_logger.LogWarning(msg);
|
|
||||||
_logger.LogDebug(msg);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
private Policy CreateCircuitBreakerPolicy(int exceptionsAllowedBeforeBreaking, int durationOfBreakInMinutes) =>
|
|
||||||
Policy.Handle<HttpRequestException>()
|
|
||||||
.CircuitBreakerAsync(
|
|
||||||
exceptionsAllowedBeforeBreaking,
|
|
||||||
TimeSpan.FromMinutes(durationOfBreakInMinutes),
|
|
||||||
(exception, duration) =>
|
|
||||||
{
|
|
||||||
// on circuit opened
|
|
||||||
_logger.LogTrace("Circuit breaker opened");
|
|
||||||
},
|
|
||||||
() =>
|
|
||||||
{
|
|
||||||
// on circuit closed
|
|
||||||
_logger.LogTrace("Circuit breaker reset");
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
public Task<string> GetStringAsync(string uri) =>
|
public Task<string> GetStringAsync(string uri) =>
|
||||||
HttpInvoker(() =>
|
HttpInvoker(() =>
|
||||||
_client.GetStringAsync(uri));
|
_client.GetStringAsync(uri));
|
||||||
|
10
src/Web/WebMVC/Infrastructure/IResilientHttpClientFactory.cs
Normal file
10
src/Web/WebMVC/Infrastructure/IResilientHttpClientFactory.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopOnContainers.WebMVC.Infrastructure
|
||||||
|
{
|
||||||
|
public interface IResilientHttpClientFactory
|
||||||
|
{
|
||||||
|
ResilientHttpClient CreateResilientHttpClient();
|
||||||
|
}
|
||||||
|
}
|
59
src/Web/WebMVC/Infrastructure/ResilientHttpClientFactory.cs
Normal file
59
src/Web/WebMVC/Infrastructure/ResilientHttpClientFactory.cs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Polly;
|
||||||
|
using System.Net.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopOnContainers.WebMVC.Infrastructure
|
||||||
|
{
|
||||||
|
public class ResilientHttpClientFactory : IResilientHttpClientFactory
|
||||||
|
{
|
||||||
|
private readonly ILogger<ResilientHttpClient> _logger;
|
||||||
|
|
||||||
|
public ResilientHttpClientFactory(ILogger<ResilientHttpClient> logger)
|
||||||
|
=>_logger = logger;
|
||||||
|
|
||||||
|
public ResilientHttpClient CreateResilientHttpClient()
|
||||||
|
=> new ResilientHttpClient(CreatePolicies(), _logger);
|
||||||
|
|
||||||
|
|
||||||
|
private Policy[] CreatePolicies()
|
||||||
|
=> new Policy[]
|
||||||
|
{
|
||||||
|
Policy.Handle<HttpRequestException>()
|
||||||
|
.WaitAndRetryAsync(
|
||||||
|
// number of retries
|
||||||
|
6,
|
||||||
|
// exponential backofff
|
||||||
|
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
|
||||||
|
// on retry
|
||||||
|
(exception, timeSpan, retryCount, context) =>
|
||||||
|
{
|
||||||
|
var msg = $"Retry {retryCount} implemented with Polly's RetryPolicy " +
|
||||||
|
$"of {context.PolicyKey} " +
|
||||||
|
$"at {context.ExecutionKey}, " +
|
||||||
|
$"due to: {exception}.";
|
||||||
|
_logger.LogWarning(msg);
|
||||||
|
_logger.LogDebug(msg);
|
||||||
|
}),
|
||||||
|
Policy.Handle<HttpRequestException>()
|
||||||
|
.CircuitBreakerAsync(
|
||||||
|
// number of exceptions before breaking circuit
|
||||||
|
5,
|
||||||
|
// time circuit opened before retry
|
||||||
|
TimeSpan.FromMinutes(1),
|
||||||
|
(exception, duration) =>
|
||||||
|
{
|
||||||
|
// on circuit opened
|
||||||
|
_logger.LogTrace("Circuit breaker opened");
|
||||||
|
},
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
// on circuit closed
|
||||||
|
_logger.LogTrace("Circuit breaker reset");
|
||||||
|
})};
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,7 @@ using System.Threading;
|
|||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Microsoft.Extensions.HealthChecks;
|
using Microsoft.Extensions.HealthChecks;
|
||||||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
|
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
|
||||||
|
using Microsoft.eShopOnContainers.WebMVC.Infrastructure;
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.WebMVC
|
namespace Microsoft.eShopOnContainers.WebMVC
|
||||||
{
|
{
|
||||||
@ -63,13 +64,8 @@ namespace Microsoft.eShopOnContainers.WebMVC
|
|||||||
|
|
||||||
if (Configuration.GetValue<string>("UseResilientHttp") == bool.TrueString)
|
if (Configuration.GetValue<string>("UseResilientHttp") == bool.TrueString)
|
||||||
{
|
{
|
||||||
services.AddSingleton(
|
services.AddTransient<IResilientHttpClientFactory, ResilientHttpClientFactory>();
|
||||||
new List<ResiliencePolicy>
|
services.AddTransient<IHttpClient, ResilientHttpClient>(sp => sp.GetService<IResilientHttpClientFactory>().CreateResilientHttpClient());
|
||||||
{
|
|
||||||
ResiliencePolicyFactory.CreateRetryPolicy(6, 2, true),
|
|
||||||
ResiliencePolicyFactory.CreateCiscuitBreakerPolicy(5, 1)
|
|
||||||
});
|
|
||||||
services.AddTransient<IHttpClient, ResilientHttpClient>();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user