Skip to content

Commit 243d9a7

Browse files
committed
Refactoring resilience implementation in order to use Polly directly when creating the resilience policies
1 parent 68ec546 commit 243d9a7

7 files changed

Lines changed: 77 additions & 141 deletions

File tree

src/BuildingBlocks/Resilience/Resilience.Http/Policies/CircuitBreakerPolicy.cs

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/BuildingBlocks/Resilience/Resilience.Http/Policies/RetryPolicy.cs

Lines changed: 0 additions & 20 deletions
This file was deleted.

src/BuildingBlocks/Resilience/Resilience.Http/ResiliencePolicyFactory.cs

Lines changed: 0 additions & 20 deletions
This file was deleted.

src/BuildingBlocks/Resilience/Resilience.Http/ResilientHttpClient.cs

Lines changed: 4 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http.Policies;
2-
using Microsoft.Extensions.Logging;
1+
using Microsoft.Extensions.Logging;
32
using Newtonsoft.Json;
43
using Polly;
54
using Polly.Wrap;
@@ -23,84 +22,14 @@ public class ResilientHttpClient : IHttpClient
2322
private ILogger<ResilientHttpClient> _logger;
2423
public HttpClient Inst => _client;
2524

26-
public ResilientHttpClient(List<Policy> policies, ILogger<ResilientHttpClient> logger)
25+
public ResilientHttpClient(Policy[] policies, ILogger<ResilientHttpClient> logger)
2726
{
2827
_client = new HttpClient();
2928
_logger = logger;
3029

3130
// Add Policies to be applied
32-
_policyWrapper = Policy.WrapAsync(policies.ToArray());
33-
}
34-
35-
public ResilientHttpClient(List<ResiliencePolicy> policies, ILogger<ResilientHttpClient> logger)
36-
{
37-
_client = new HttpClient();
38-
_logger = logger;
39-
40-
// Add Policies to be applied
41-
_policyWrapper = Policy.WrapAsync(GeneratePolicies(policies));
42-
}
43-
44-
private Policy[] GeneratePolicies(IList<ResiliencePolicy> policies)
45-
{
46-
var pollyPolicies = new List<Policy>();
47-
48-
foreach (var policy in policies)
49-
{
50-
switch (policy)
51-
{
52-
case RetryPolicy retryPolicy:
53-
pollyPolicies.Add(
54-
CreateRetryPolicy(
55-
retryPolicy.Retries,
56-
retryPolicy.BackoffSeconds,
57-
retryPolicy.ExponentialBackoff));
58-
break;
59-
60-
case CircuitBreakerPolicy circuitPolicy:
61-
pollyPolicies.Add(
62-
CreateCircuitBreakerPolicy(
63-
circuitPolicy.ExceptionsAllowedBeforeBreaking,
64-
circuitPolicy.DurationOfBreakInMinutes));
65-
break;
66-
}
67-
}
68-
69-
return pollyPolicies.ToArray();
70-
}
71-
72-
private Policy CreateRetryPolicy(int retries, int backoffSeconds, bool exponentialBackoff) =>
73-
Policy.Handle<HttpRequestException>()
74-
.WaitAndRetryAsync(
75-
retries,
76-
retryAttempt => exponentialBackoff ? TimeSpan.FromSeconds(Math.Pow(backoffSeconds, retryAttempt)) : TimeSpan.FromSeconds(backoffSeconds),
77-
(exception, timeSpan, retryCount, context) =>
78-
{
79-
var msg = $"Retry {retryCount} implemented with Polly's RetryPolicy " +
80-
$"of {context.PolicyKey} " +
81-
$"at {context.ExecutionKey}, " +
82-
$"due to: {exception}.";
83-
_logger.LogWarning(msg);
84-
_logger.LogDebug(msg);
85-
}
86-
);
87-
88-
private Policy CreateCircuitBreakerPolicy(int exceptionsAllowedBeforeBreaking, int durationOfBreakInMinutes) =>
89-
Policy.Handle<HttpRequestException>()
90-
.CircuitBreakerAsync(
91-
exceptionsAllowedBeforeBreaking,
92-
TimeSpan.FromMinutes(durationOfBreakInMinutes),
93-
(exception, duration) =>
94-
{
95-
// on circuit opened
96-
_logger.LogTrace("Circuit breaker opened");
97-
},
98-
() =>
99-
{
100-
// on circuit closed
101-
_logger.LogTrace("Circuit breaker reset");
102-
}
103-
);
31+
_policyWrapper = Policy.WrapAsync(policies);
32+
}
10433

10534
public Task<string> GetStringAsync(string uri) =>
10635
HttpInvoker(() =>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
2+
using System;
3+
4+
namespace Microsoft.eShopOnContainers.WebMVC.Infrastructure
5+
{
6+
public interface IResilientHttpClientFactory
7+
{
8+
ResilientHttpClient CreateResilientHttpClient();
9+
}
10+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
2+
using Microsoft.Extensions.Logging;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Threading.Tasks;
7+
using Polly;
8+
using System.Net.Http;
9+
10+
namespace Microsoft.eShopOnContainers.WebMVC.Infrastructure
11+
{
12+
public class ResilientHttpClientFactory : IResilientHttpClientFactory
13+
{
14+
private readonly ILogger<ResilientHttpClient> _logger;
15+
16+
public ResilientHttpClientFactory(ILogger<ResilientHttpClient> logger)
17+
=>_logger = logger;
18+
19+
public ResilientHttpClient CreateResilientHttpClient()
20+
=> new ResilientHttpClient(CreatePolicies(), _logger);
21+
22+
23+
private Policy[] CreatePolicies()
24+
=> new Policy[]
25+
{
26+
Policy.Handle<HttpRequestException>()
27+
.WaitAndRetryAsync(
28+
// number of retries
29+
6,
30+
// exponential backofff
31+
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
32+
// on retry
33+
(exception, timeSpan, retryCount, context) =>
34+
{
35+
var msg = $"Retry {retryCount} implemented with Polly's RetryPolicy " +
36+
$"of {context.PolicyKey} " +
37+
$"at {context.ExecutionKey}, " +
38+
$"due to: {exception}.";
39+
_logger.LogWarning(msg);
40+
_logger.LogDebug(msg);
41+
}),
42+
Policy.Handle<HttpRequestException>()
43+
.CircuitBreakerAsync(
44+
// number of exceptions before breaking circuit
45+
5,
46+
// time circuit opened before retry
47+
TimeSpan.FromMinutes(1),
48+
(exception, duration) =>
49+
{
50+
// on circuit opened
51+
_logger.LogTrace("Circuit breaker opened");
52+
},
53+
() =>
54+
{
55+
// on circuit closed
56+
_logger.LogTrace("Circuit breaker reset");
57+
})};
58+
}
59+
}

src/Web/WebMVC/Startup.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using Microsoft.Extensions.Options;
1717
using Microsoft.Extensions.HealthChecks;
1818
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
19+
using Microsoft.eShopOnContainers.WebMVC.Infrastructure;
1920

2021
namespace Microsoft.eShopOnContainers.WebMVC
2122
{
@@ -63,19 +64,14 @@ public void ConfigureServices(IServiceCollection services)
6364

6465
if (Configuration.GetValue<string>("UseResilientHttp") == bool.TrueString)
6566
{
66-
services.AddSingleton(
67-
new List<ResiliencePolicy>
68-
{
69-
ResiliencePolicyFactory.CreateRetryPolicy(6, 2, true),
70-
ResiliencePolicyFactory.CreateCiscuitBreakerPolicy(5, 1)
71-
});
72-
services.AddTransient<IHttpClient, ResilientHttpClient>();
67+
services.AddTransient<IResilientHttpClientFactory, ResilientHttpClientFactory>();
68+
services.AddTransient<IHttpClient, ResilientHttpClient>(sp => sp.GetService<IResilientHttpClientFactory>().CreateResilientHttpClient());
7369
}
7470
else
7571
{
7672
services.AddTransient<IHttpClient, StandardHttpClient>();
7773
}
78-
}
74+
}
7975

8076
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
8177
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)

0 commit comments

Comments
 (0)