HttpClient retry on HTTP timeout with Polly and IHttpClientBuilder

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:

About briancaos

Developer at Pentia A/S since 2003. Have developed Web Applications using Sitecore Since Sitecore 4.1.
This entry was posted in .net, .NET Core, c# and tagged , , , . Bookmark the permalink.

2 Responses to HttpClient retry on HTTP timeout with Polly and IHttpClientBuilder

  1. Fotis Spatharakis says:

    Trying to understand the way Polly works with HttpClient and timeouts, I came across your article and It make things very clear. The nice thing is it can be further pushed to create a named HttpClient by using the:

    services.AddHttpClient()

    And then use the policies as you described. The implementation would something like

    public class MyClientImplementation: IMyClientInterface
    {
    private readonly HttpClient _httpClient;
    public MyClientImplentation(
    HttpClient client
    )
    {
    _client = client;
    }
    }

    One more interest and usefult thing I found out is adding these policies in Polly’s registry in order to be reused. E.g.:

    var registry = services.AddPolicyRegistry();
    registry.Add(“policy”, Policy.TimeoutAsync(2));

    Liked by 1 person

  2. briancaos says:

    Nice! Thanks.

    Like

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 )

Google photo

You are commenting using your Google 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 )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.