2017-03-17 10:00:18 +01:00
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
using Polly;
|
|
|
|
|
using Polly.Wrap;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Net.Http;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
namespace WebMVC.Services.Utilities
|
|
|
|
|
{
|
2017-03-17 13:12:34 +01:00
|
|
|
|
public class HttpApiClientWrapper : IHttpClient
|
2017-03-17 10:00:18 +01:00
|
|
|
|
{
|
|
|
|
|
private HttpClient _client;
|
|
|
|
|
private PolicyWrap _policyWrapper;
|
|
|
|
|
private ILogger _logger;
|
|
|
|
|
public HttpClient Inst => _client;
|
2017-03-17 13:12:34 +01:00
|
|
|
|
public HttpApiClientWrapper()
|
2017-03-17 10:00:18 +01:00
|
|
|
|
{
|
|
|
|
|
_client = new HttpClient();
|
2017-03-17 13:12:34 +01:00
|
|
|
|
_logger = new LoggerFactory().CreateLogger(nameof(HttpApiClientWrapper));
|
2017-03-17 10:00:18 +01:00
|
|
|
|
|
|
|
|
|
// Add Policies to be applied
|
|
|
|
|
_policyWrapper = Policy.WrapAsync(
|
|
|
|
|
CreateRetryPolicy(),
|
|
|
|
|
CreateCircuitBreakerPolicy()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-20 14:18:20 -04:00
|
|
|
|
private Policy CreateCircuitBreakerPolicy() =>
|
|
|
|
|
Policy.Handle<HttpRequestException>()
|
|
|
|
|
.CircuitBreakerAsync(
|
|
|
|
|
// number of exceptions before breaking circuit
|
|
|
|
|
3,
|
|
|
|
|
// 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");
|
|
|
|
|
}
|
|
|
|
|
);
|
2017-03-17 10:00:18 +01:00
|
|
|
|
|
2017-03-20 14:18:20 -04:00
|
|
|
|
private Policy CreateRetryPolicy() =>
|
|
|
|
|
Policy.Handle<HttpRequestException>()
|
|
|
|
|
.WaitAndRetryAsync(
|
|
|
|
|
// number of retries
|
|
|
|
|
3,
|
|
|
|
|
// exponential backofff
|
|
|
|
|
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
|
|
|
|
|
// on retry
|
|
|
|
|
(exception, timeSpan, retryCount, context) =>
|
|
|
|
|
{
|
|
|
|
|
_logger.LogTrace($"Retry {retryCount} " +
|
|
|
|
|
$"of {context.PolicyKey} " +
|
|
|
|
|
$"at {context.ExecutionKey}, " +
|
|
|
|
|
$"due to: {exception}.");
|
|
|
|
|
}
|
|
|
|
|
);
|
2017-03-17 10:00:18 +01:00
|
|
|
|
|
2017-03-20 14:18:20 -04:00
|
|
|
|
// Notice that these (and other methods below) are Task
|
|
|
|
|
// returning asynchronous methods. But, they do not
|
|
|
|
|
// have the 'async' modifier, and do not contain
|
|
|
|
|
// any 'await statements. In each of these methods,
|
|
|
|
|
// the only asynchronous call is the last (or only)
|
|
|
|
|
// statement of the method. In those instances,
|
|
|
|
|
// a Task returning method that does not use the
|
|
|
|
|
// async modifier is preferred. The compiler generates
|
|
|
|
|
// synchronous code for this method, but returns the
|
|
|
|
|
// task from the underlying asynchronous method. The
|
|
|
|
|
// generated code does not contain the state machine
|
|
|
|
|
// generated for asynchronous methods.
|
|
|
|
|
public Task<string> GetStringAsync(string uri) =>
|
|
|
|
|
HttpInvoker(() => _client.GetStringAsync(uri));
|
|
|
|
|
|
|
|
|
|
public Task<HttpResponseMessage> PostAsync<T>(string uri, T item) =>
|
2017-03-17 10:00:18 +01:00
|
|
|
|
// a new StringContent must be created for each retry
|
|
|
|
|
// as it is disposed after each call
|
2017-03-20 14:18:20 -04:00
|
|
|
|
HttpInvoker(() =>_client.PostAsync(uri,
|
|
|
|
|
new StringContent(JsonConvert.SerializeObject(item),
|
|
|
|
|
System.Text.Encoding.UTF8, "application/json")));
|
2017-03-17 10:00:18 +01:00
|
|
|
|
|
2017-03-20 14:18:20 -04:00
|
|
|
|
public Task<HttpResponseMessage> DeleteAsync(string uri) =>
|
|
|
|
|
HttpInvoker(() => _client.DeleteAsync(uri));
|
2017-03-17 10:00:18 +01:00
|
|
|
|
|
2017-03-20 14:18:20 -04:00
|
|
|
|
|
|
|
|
|
private Task<T> HttpInvoker<T>(Func<Task<T>> action) =>
|
2017-03-17 10:00:18 +01:00
|
|
|
|
// Executes the action applying all
|
|
|
|
|
// the policies defined in the wrapper
|
2017-03-20 14:18:20 -04:00
|
|
|
|
_policyWrapper.ExecuteAsync(() => action());
|
2017-03-17 10:00:18 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|