Skip to content

Commit efe496e

Browse files
committed
ResilientHttp client policies fixed
1 parent 4ca7dfd commit efe496e

3 files changed

Lines changed: 75 additions & 65 deletions

File tree

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

Lines changed: 73 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using Polly;
44
using Polly.Wrap;
55
using System;
6+
using System.Collections.Concurrent;
67
using System.Collections.Generic;
78
using System.Linq;
89
using System.Net;
@@ -21,13 +22,13 @@ public class ResilientHttpClient : IHttpClient
2122
{
2223
private readonly HttpClient _client;
2324
private readonly ILogger<ResilientHttpClient> _logger;
24-
private PolicyWrap _policyWrap;
25+
private ConcurrentDictionary<string, PolicyWrap> _policyWrappers;
2526

26-
public ResilientHttpClient(IEnumerable<Policy> policies, ILogger<ResilientHttpClient> logger)
27+
public ResilientHttpClient(ILogger<ResilientHttpClient> logger)
2728
{
2829
_client = new HttpClient();
2930
_logger = logger;
30-
_policyWrap = Policy.Wrap(policies.ToArray());
31+
_policyWrappers = new ConcurrentDictionary<string, PolicyWrap>();
3132
}
3233

3334

@@ -93,43 +94,49 @@ private Task<HttpResponseMessage> DoPostPutAsync<T>(HttpMethod method, string ur
9394
// as it is disposed after each call
9495
var origin = GetOriginFromUri(uri);
9596

96-
return HttpInvoker(origin, async () =>
97-
{
98-
var requestMessage = new HttpRequestMessage(method, uri);
97+
return HttpInvoker(origin, () =>
98+
{
99+
var requestMessage = new HttpRequestMessage(method, uri);
99100

100-
requestMessage.Content = new StringContent(JsonConvert.SerializeObject(item), System.Text.Encoding.UTF8, "application/json");
101+
requestMessage.Content = new StringContent(JsonConvert.SerializeObject(item), System.Text.Encoding.UTF8, "application/json");
101102

102-
if (authorizationToken != null)
103-
{
104-
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
105-
}
103+
if (authorizationToken != null)
104+
{
105+
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
106+
}
106107

107-
if (requestId != null)
108-
{
109-
requestMessage.Headers.Add("x-requestid", requestId);
110-
}
108+
if (requestId != null)
109+
{
110+
requestMessage.Headers.Add("x-requestid", requestId);
111+
}
111112

112-
var response = await _client.SendAsync(requestMessage);
113+
var response = _client.SendAsync(requestMessage).Result;
113114

114-
// raise exception if HttpResponseCode 500
115-
// needed for circuit breaker to track fails
115+
// raise exception if HttpResponseCode 500
116+
// needed for circuit breaker to track fails
116117

117-
if (response.StatusCode == HttpStatusCode.InternalServerError)
118-
{
119-
throw new HttpRequestException();
120-
}
118+
if (response.StatusCode == HttpStatusCode.InternalServerError)
119+
{
120+
throw new HttpRequestException();
121+
}
121122

122-
return response;
123-
});
123+
return Task.FromResult(response);
124+
});
124125
}
125126

126-
private Task<T> HttpInvoker<T>(string origin, Func<Task<T>> action)
127+
private async Task<T> HttpInvoker<T>(string origin, Func<Task<T>> action)
127128
{
128129
var normalizedOrigin = NormalizeOrigin(origin);
129130

131+
if (!_policyWrappers.TryGetValue(normalizedOrigin, out PolicyWrap policyWrap))
132+
{
133+
policyWrap = Policy.Wrap(CreatePolicies());
134+
_policyWrappers.TryAdd(normalizedOrigin, policyWrap);
135+
}
136+
130137
// Executes the action applying all
131138
// the policies defined in the wrapper
132-
return _policyWrap.ExecuteAsync(() => action(), new Context(normalizedOrigin));
139+
return await policyWrap.Execute(action, new Context(normalizedOrigin));
133140
}
134141

135142

@@ -146,5 +153,45 @@ private static string GetOriginFromUri(string uri)
146153

147154
return origin;
148155
}
156+
157+
158+
private Policy[] CreatePolicies()
159+
{
160+
return new Policy[]
161+
{
162+
Policy.Handle<HttpRequestException>()
163+
.WaitAndRetry(
164+
// number of retries
165+
6,
166+
// exponential backofff
167+
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
168+
// on retry
169+
(exception, timeSpan, retryCount, context) =>
170+
{
171+
var msg = $"Retry {retryCount} implemented with Polly's RetryPolicy " +
172+
$"of {context.PolicyKey} " +
173+
$"at {context.ExecutionKey}, " +
174+
$"due to: {exception}.";
175+
_logger.LogWarning(msg);
176+
_logger.LogDebug(msg);
177+
}),
178+
Policy.Handle<HttpRequestException>()
179+
.CircuitBreaker(
180+
// number of exceptions before breaking circuit
181+
5,
182+
// time circuit opened before retry
183+
TimeSpan.FromMinutes(1),
184+
(exception, duration) =>
185+
{
186+
// on circuit opened
187+
_logger.LogTrace("Circuit breaker opened");
188+
},
189+
() =>
190+
{
191+
// on circuit closed
192+
_logger.LogTrace("Circuit breaker reset");
193+
})
194+
};
195+
}
149196
}
150197
}

src/Web/WebMVC/Infrastructure/ResilientHttpClientFactory.cs

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -17,43 +17,6 @@ public ResilientHttpClientFactory(ILogger<ResilientHttpClient> logger)
1717
=>_logger = logger;
1818

1919
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-
})};
20+
=> new ResilientHttpClient(_logger);
5821
}
5922
}

src/Web/WebMVC/Startup.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public void ConfigureServices(IServiceCollection services)
7474

7575
if (Configuration.GetValue<string>("UseResilientHttp") == bool.TrueString)
7676
{
77-
services.AddTransient<IResilientHttpClientFactory, ResilientHttpClientFactory>();
77+
services.AddSingleton<IResilientHttpClientFactory, ResilientHttpClientFactory>();
7878
services.AddSingleton<IHttpClient, ResilientHttpClient>(sp => sp.GetService<IResilientHttpClientFactory>().CreateResilientHttpClient());
7979
}
8080
else

0 commit comments

Comments
 (0)