Quantcast
Channel: briancaos – Brian Pedersen's Sitecore and .NET Blog
Viewing all articles
Browse latest Browse all 276

HttpClient retry on HTTP timeout with Polly and IHttpClientBuilder

$
0
0

The Polly retry library and the IHttpClientBuilder is a match made in heaven as it defines all the retry logic at startup. The actual HttpClient  calls are therefore untouched by any retry code.

The retry logic is called policies, and they determine how and in what circumstances a retry must be done.

Retrying on HTTP timeouts (where the caller does not respond) differs slightly from other HTTP errors (where the caller returns 404 Not Found or 500 errors). This is because the HttpClient does not receive an response code, but throws a TimeoutRejectedException when the call time outs.

This requires your configuration to make a retry policy and wrap this policy in a timeout policy.

But enough talk, lets code.

STEP 1: THE NUGET PACKAGES

You need the following packages:

  • Polly
  • Microsoft.Extensions.Http.Polly

STEP 2: CONFIGURE IHttpClientBuilder AND POLLY POLICIES IN THE STARTUP

In the startup.cs, add a HttpClient to the services and configure the retry policies, and then wrap the retry policies in a timeout policy. This is an example from a startup.cs file:

public static IHostBuilder CreateHostBuilder(string[] args)
{
  var host = Host.CreateDefaultBuilder(args);
  host.ConfigureServices((hostContext, services) =>
  {
    // ...
    // ...
    services.AddHttpClient("HttpClient")
      .AddPolicyHandler(GetRetryPolicy())
      .AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(5));
    });
    // ...
    // ...
  } 
  return host;
}

private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
  return HttpPolicyExtensions
    .HandleTransientHttpError()
    .Or<TimeoutRejectedException>()
    .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(30));
}

What’s happening here?

The services.AddHttpClient creates a new HttpClient.

The First policy handler added is the retry policy. Please note that the retry policy will also retry on TimeoutRejectedExceptions. This retry policy will retry 3 times with 30 seconds delay.

The next policy handler is the timeout handler. This handler will throw a TimeoutRejectedException when the url called have been unresponsive for 5 seconds.

STEP 3: USE THE IHttpClientFactory IN THE CALLING CLASS

There is no Polly code in the class that does the http calls:

namespace MyCode
{
  public class MyClass
  {
    private readonly IHttpClientFactory _clientFactory;
 
    public MyClass(IHttpClientFactory clientFactory)
    {
      _clientFactory = clientFactory;
    }
 
    public async Task<string> Get(string url)
    {
      string authUserName = "user";
      string authPassword = "password";
 
      var httpClient = _clientFactory.CreateClient("HttpClient");
      // If you do not have basic authentication, you may skip these lines
      var authToken = Encoding.ASCII.GetBytes($"{authUserName}:{authPassword}");
      httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(authToken));
 
      // The actual Get method
      using (var result = await httpClient.GetAsync($"{url}"))
      {
        string content = await result.Content.ReadAsStringAsync();
        return content;
      }
    }
  }
}

The httpClient.GetAsync() will retry the call automatically if any of the conditions described in the GetRetryPolicy() occurs. It will only return after the call is either successful or the retry count is met.

FINAL NOTE: DO NOT SET HttpClient.Timeout

The HttpClient.Timeout will set the global timeout, i.e. the overall timeout, including polly retries. So if you set this timeout you will receive a TaskCanceledException or OperationCanceledException instead of the TimeoutRejectedException, and those exceptions cannot be caught by the timeout policy.

MORE TO READ:


Viewing all articles
Browse latest Browse all 276

Trending Articles