A day with .Net

My day to day experince in .net

Resilient HTTP call with retry and exponential back-off – Micro-services architecture.

Posted by vivekcek on January 11, 2018

During the designing of a microservice architecture based application, i came across a scenario in which i need to make sure the http call to other services need to retry for ensuring resilence.

The approach i tried is retry the http call when request exception occur. And each retry is performed after a particular time interwell, which is exponenetial in nature.

To implement this i used a nuget package named polly.

This is the Resilient HTTP class we wrote, You can use an interface and dependency injection for productions app.

public interface IHttpClient
{
Task<string> GetStringAsync(string uri, string authorizationToken = null,
string authorizationMethod = "Bearer");
Task<HttpResponseMessage> PostAsync<T>(string uri, T item,
string authorizationToken = null, string requestId = null,
string authorizationMethod = "Bearer");
Task<HttpResponseMessage> DeleteAsync(string uri,
string authorizationToken = null, string requestId = null,
string authorizationMethod = "Bearer");
}
public class ResilientHttpClient : IHttpClient
{
private HttpClient _client;
private PolicyWrap _policyWrapper;
private ILogger<ResilientHttpClient> _logger;
public ResilientHttpClient(Policy[] policies,
ILogger<ResilientHttpClient> logger)
{
_client = new HttpClient();
_logger = logger;
// Add Policies to be applied
_policyWrapper = Policy.WrapAsync(policies);
}
private Task<T> HttpInvoker<T>(Func<Task<T>> action)
{
// Executes the action applying all
// the policies defined in the wrapper
return _policyWrapper.ExecuteAsync(() => action());
}
public Task<string> GetStringAsync(string uri,
string authorizationToken = null,
string authorizationMethod = "Bearer")
{
return HttpInvoker(async () =>
{
var requestMessage = new HttpRequestMessage(HttpMethod.Get, uri);
var response = await _client.SendAsync(requestMessage);
return await response.Content.ReadAsStringAsync();
});
}
}

Now in your WebApi’s Startup.cs write below code.

// Startup.cs class
if (Configuration.GetValue<string>("UseResilientHttp") == bool.TrueString)
{
services.AddTransient<IResilientHttpClientFactory,
ResilientHttpClientFactory>();
services.AddSingleton<IHttpClient,
ResilientHttpClient>(sp =>
sp.GetService<IResilientHttpClientFactory>().
CreateResilientHttpClient());
}
else
{
services.AddSingleton<IHttpClient, StandardHttpClient>();
}

public ResilientHttpClient CreateResilientHttpClient()
=> new ResilientHttpClient(CreatePolicies(), _logger);
// Other code
private Policy[] CreatePolicies()
=> new Policy[]
{
Policy.Handle<HttpRequestException>()
.WaitAndRetryAsync(
// number of retries
6,
// exponential backoff
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
// on retry
(exception, timeSpan, retryCount, context) =>
{
var msg = $"Retry {retryCount} implemented with Pollys
RetryPolicy " +
$"of {context.PolicyKey} " +
$"at {context.ExecutionKey}, " +
$"due to: {exception}.";
_logger.LogWarning(msg);
_logger.LogDebug(msg);
}),
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s