Skip to content
This repository has been archived by the owner on Nov 17, 2023. It is now read-only.

Commit

Permalink
Refactoring resilience implementation in order to use Polly directly …
Browse files Browse the repository at this point in the history
…when creating the resilience policies
  • Loading branch information
dsrodenas committed Apr 7, 2017
1 parent e05cbfa commit 0c317d5
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 141 deletions.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -23,84 +22,14 @@ public class ResilientHttpClient : IHttpClient
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(() =>
Expand Down
10 changes: 10 additions & 0 deletions src/Web/WebMVC/Infrastructure/IResilientHttpClientFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using System;

namespace Microsoft.eShopOnContainers.WebMVC.Infrastructure
{
public interface IResilientHttpClientFactory
{
ResilientHttpClient CreateResilientHttpClient();
}
}
59 changes: 59 additions & 0 deletions src/Web/WebMVC/Infrastructure/ResilientHttpClientFactory.cs
Original file line number Diff line number Diff line change
@@ -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");
})};
}
}
12 changes: 4 additions & 8 deletions src/Web/WebMVC/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using Microsoft.Extensions.Options;
using Microsoft.Extensions.HealthChecks;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.eShopOnContainers.WebMVC.Infrastructure;

namespace Microsoft.eShopOnContainers.WebMVC
{
Expand Down Expand Up @@ -63,19 +64,14 @@ public void ConfigureServices(IServiceCollection services)

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)
Expand Down

0 comments on commit 0c317d5

Please sign in to comment.