From 0c317d56f3c8937f6823cf1b45f5683397274815 Mon Sep 17 00:00:00 2001 From: dsanz Date: Fri, 7 Apr 2017 12:48:22 +0200 Subject: [PATCH] Refactoring resilience implementation in order to use Polly directly when creating the resilience policies --- .../Policies/CircuitBreakerPolicy.cs | 18 ----- .../Resilience.Http/Policies/RetryPolicy.cs | 20 ----- .../ResiliencePolicyFactory.cs | 20 ----- .../Resilience.Http/ResilientHttpClient.cs | 79 +------------------ .../IResilientHttpClientFactory.cs | 10 +++ .../ResilientHttpClientFactory.cs | 59 ++++++++++++++ src/Web/WebMVC/Startup.cs | 12 +-- 7 files changed, 77 insertions(+), 141 deletions(-) delete mode 100644 src/BuildingBlocks/Resilience/Resilience.Http/Policies/CircuitBreakerPolicy.cs delete mode 100644 src/BuildingBlocks/Resilience/Resilience.Http/Policies/RetryPolicy.cs delete mode 100644 src/BuildingBlocks/Resilience/Resilience.Http/ResiliencePolicyFactory.cs create mode 100644 src/Web/WebMVC/Infrastructure/IResilientHttpClientFactory.cs create mode 100644 src/Web/WebMVC/Infrastructure/ResilientHttpClientFactory.cs diff --git a/src/BuildingBlocks/Resilience/Resilience.Http/Policies/CircuitBreakerPolicy.cs b/src/BuildingBlocks/Resilience/Resilience.Http/Policies/CircuitBreakerPolicy.cs deleted file mode 100644 index 0f64f7403..000000000 --- a/src/BuildingBlocks/Resilience/Resilience.Http/Policies/CircuitBreakerPolicy.cs +++ /dev/null @@ -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; } - } -} diff --git a/src/BuildingBlocks/Resilience/Resilience.Http/Policies/RetryPolicy.cs b/src/BuildingBlocks/Resilience/Resilience.Http/Policies/RetryPolicy.cs deleted file mode 100644 index ac2bf3807..000000000 --- a/src/BuildingBlocks/Resilience/Resilience.Http/Policies/RetryPolicy.cs +++ /dev/null @@ -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; } - } -} diff --git a/src/BuildingBlocks/Resilience/Resilience.Http/ResiliencePolicyFactory.cs b/src/BuildingBlocks/Resilience/Resilience.Http/ResiliencePolicyFactory.cs deleted file mode 100644 index 8c2e5f8ab..000000000 --- a/src/BuildingBlocks/Resilience/Resilience.Http/ResiliencePolicyFactory.cs +++ /dev/null @@ -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); - } - } -} diff --git a/src/BuildingBlocks/Resilience/Resilience.Http/ResilientHttpClient.cs b/src/BuildingBlocks/Resilience/Resilience.Http/ResilientHttpClient.cs index f091363ac..2ccc84aaa 100644 --- a/src/BuildingBlocks/Resilience/Resilience.Http/ResilientHttpClient.cs +++ b/src/BuildingBlocks/Resilience/Resilience.Http/ResilientHttpClient.cs @@ -1,5 +1,4 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http.Policies; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Polly; using Polly.Wrap; @@ -23,84 +22,14 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http private ILogger _logger; public HttpClient Inst => _client; - public ResilientHttpClient(List policies, ILogger logger) + public ResilientHttpClient(Policy[] policies, ILogger logger) { _client = new HttpClient(); _logger = logger; // Add Policies to be applied - _policyWrapper = Policy.WrapAsync(policies.ToArray()); - } - - public ResilientHttpClient(List policies, ILogger logger) - { - _client = new HttpClient(); - _logger = logger; - - // Add Policies to be applied - _policyWrapper = Policy.WrapAsync(GeneratePolicies(policies)); - } - - private Policy[] GeneratePolicies(IList policies) - { - var pollyPolicies = new List(); - - 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() - .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() - .CircuitBreakerAsync( - exceptionsAllowedBeforeBreaking, - TimeSpan.FromMinutes(durationOfBreakInMinutes), - (exception, duration) => - { - // on circuit opened - _logger.LogTrace("Circuit breaker opened"); - }, - () => - { - // on circuit closed - _logger.LogTrace("Circuit breaker reset"); - } - ); + _policyWrapper = Policy.WrapAsync(policies); + } public Task GetStringAsync(string uri) => HttpInvoker(() => diff --git a/src/Web/WebMVC/Infrastructure/IResilientHttpClientFactory.cs b/src/Web/WebMVC/Infrastructure/IResilientHttpClientFactory.cs new file mode 100644 index 000000000..aa1d31c9b --- /dev/null +++ b/src/Web/WebMVC/Infrastructure/IResilientHttpClientFactory.cs @@ -0,0 +1,10 @@ +using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; +using System; + +namespace Microsoft.eShopOnContainers.WebMVC.Infrastructure +{ + public interface IResilientHttpClientFactory + { + ResilientHttpClient CreateResilientHttpClient(); + } +} \ No newline at end of file diff --git a/src/Web/WebMVC/Infrastructure/ResilientHttpClientFactory.cs b/src/Web/WebMVC/Infrastructure/ResilientHttpClientFactory.cs new file mode 100644 index 000000000..8efadf366 --- /dev/null +++ b/src/Web/WebMVC/Infrastructure/ResilientHttpClientFactory.cs @@ -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 _logger; + + public ResilientHttpClientFactory(ILogger logger) + =>_logger = logger; + + public ResilientHttpClient CreateResilientHttpClient() + => new ResilientHttpClient(CreatePolicies(), _logger); + + + private Policy[] CreatePolicies() + => new Policy[] + { + Policy.Handle() + .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() + .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"); + })}; + } +} diff --git a/src/Web/WebMVC/Startup.cs b/src/Web/WebMVC/Startup.cs index 72b764925..5a2f0d8fe 100644 --- a/src/Web/WebMVC/Startup.cs +++ b/src/Web/WebMVC/Startup.cs @@ -16,6 +16,7 @@ using System.Threading; using Microsoft.Extensions.Options; using Microsoft.Extensions.HealthChecks; using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; +using Microsoft.eShopOnContainers.WebMVC.Infrastructure; namespace Microsoft.eShopOnContainers.WebMVC { @@ -63,19 +64,14 @@ namespace Microsoft.eShopOnContainers.WebMVC if (Configuration.GetValue("UseResilientHttp") == bool.TrueString) { - services.AddSingleton( - new List - { - ResiliencePolicyFactory.CreateRetryPolicy(6, 2, true), - ResiliencePolicyFactory.CreateCiscuitBreakerPolicy(5, 1) - }); - services.AddTransient(); + services.AddTransient(); + services.AddTransient(sp => sp.GetService().CreateResilientHttpClient()); } else { services.AddTransient(); } - } + } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)