Browse Source

Refactoring resilience implementation in order to use Polly directly when creating the resilience policies

pull/173/head
dsanz 7 years ago
parent
commit
0c317d56f3
7 changed files with 77 additions and 141 deletions
  1. +0
    -18
      src/BuildingBlocks/Resilience/Resilience.Http/Policies/CircuitBreakerPolicy.cs
  2. +0
    -20
      src/BuildingBlocks/Resilience/Resilience.Http/Policies/RetryPolicy.cs
  3. +0
    -20
      src/BuildingBlocks/Resilience/Resilience.Http/ResiliencePolicyFactory.cs
  4. +4
    -75
      src/BuildingBlocks/Resilience/Resilience.Http/ResilientHttpClient.cs
  5. +10
    -0
      src/Web/WebMVC/Infrastructure/IResilientHttpClientFactory.cs
  6. +59
    -0
      src/Web/WebMVC/Infrastructure/ResilientHttpClientFactory.cs
  7. +4
    -8
      src/Web/WebMVC/Startup.cs

+ 0
- 18
src/BuildingBlocks/Resilience/Resilience.Http/Policies/CircuitBreakerPolicy.cs View File

@ -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; }
}
}

+ 0
- 20
src/BuildingBlocks/Resilience/Resilience.Http/Policies/RetryPolicy.cs View File

@ -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; }
}
}

+ 0
- 20
src/BuildingBlocks/Resilience/Resilience.Http/ResiliencePolicyFactory.cs View File

@ -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);
}
}
}

+ 4
- 75
src/BuildingBlocks/Resilience/Resilience.Http/ResilientHttpClient.cs View File

@ -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<ResilientHttpClient> _logger;
public HttpClient Inst => _client;
public ResilientHttpClient(List<Policy> policies, ILogger<ResilientHttpClient> logger)
public ResilientHttpClient(Policy[] policies, ILogger<ResilientHttpClient> logger)
{
_client = new HttpClient();
_logger = logger;
// Add Policies to be applied
_policyWrapper = Policy.WrapAsync(policies.ToArray());
}
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");
}
);
_policyWrapper = Policy.WrapAsync(policies);
}
public Task<string> GetStringAsync(string uri) =>
HttpInvoker(() =>


+ 10
- 0
src/Web/WebMVC/Infrastructure/IResilientHttpClientFactory.cs View File

@ -0,0 +1,10 @@
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using System;
namespace Microsoft.eShopOnContainers.WebMVC.Infrastructure
{
public interface IResilientHttpClientFactory
{
ResilientHttpClient CreateResilientHttpClient();
}
}

+ 59
- 0
src/Web/WebMVC/Infrastructure/ResilientHttpClientFactory.cs View 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");
})};
}
}

+ 4
- 8
src/Web/WebMVC/Startup.cs View File

@ -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<string>("UseResilientHttp") == bool.TrueString)
{
services.AddSingleton(
new List<ResiliencePolicy>
{
ResiliencePolicyFactory.CreateRetryPolicy(6, 2, true),
ResiliencePolicyFactory.CreateCiscuitBreakerPolicy(5, 1)
});
services.AddTransient<IHttpClient, ResilientHttpClient>();
services.AddTransient<IResilientHttpClientFactory, ResilientHttpClientFactory>();
services.AddTransient<IHttpClient, ResilientHttpClient>(sp => sp.GetService<IResilientHttpClientFactory>().CreateResilientHttpClient());
}
else
{
services.AddTransient<IHttpClient, StandardHttpClient>();
}
}
}
// 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)


Loading…
Cancel
Save