Looking for a specific post? Checkout the Blog Index.

By Kimserey Lam with

Polly Httpclient Resilience In Dotnet

Sep 7th, 2018 - written by Kimserey with .

Few weeks ago I explained [how to use the new HttpClientFactory. This freed ourselves from managing the confusing lifecycle of a HttpClient and at the same time allowed us to setup commmon options like base address for all HttpClient injections in our classes. Today we will see how we can setup timeout and retry policies for the HttpClient in in ASP NET Core using Polly.

HttpClient

In the previous blog post we saw how we could setup a HttpClient and register it to the service collection.

Here is a simple service client which will be injected throughout the application:

1
2
3
4
public interface IMyApiClient
{
    Task<HttpResponseMessage> Get();
}

And here is the implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MyApiClient : IMyApiClient
{
    private readonly HttpClient _client;

    public ApiClient(HttpClient client)
    {
        _client = client;
    }

    public Task<HttpResponseMessage> Get()
    {
        return _client.GetAsync();
    }
}

We can then register it to the service collection:

1
2
3
4
5
6
7
8
public void ConfigureServices(IServiceCollection services)
{
    services
        .AddHttpClient<IMyApiClient, MyApiClient>(opts =>
        {
            opts.BaseAddress = new Uri("http://localhost:5100");
        });
}

We are now able to inject IMyApiClient in our services or controllers, and once we call .Get(), we will be hitting GET http://localhost:5100 using a HttpClient managed by the framework. Now apart from the lifecycle management, another major benefits from the abstraction is the ability to make the client more resilient by introduce Polly.

Polly

Polly can be installed together with the ASP NET Core extensions by installing the following package:

1
Microsoft.Extensions.Http.Polly

The extensions library provides a set of extensions on the IHttpClientBuilder like the following:

1
public static IHttpClientBuilder AddPolicyHandler(this IHttpClientBuilder builder, IAsyncPolicy<HttpResponseMessage> policy);

Which can be used to like so:

1
2
3
4
5
6
services
    .AddHttpClient<IApiClient, ApiClient>(opts =>
    {
        opts.BaseAddress = new Uri("http://localhost:5100");
    })
    .AddPolicyHandler(MyPolicy);

IAsyncPolicy<T> is the policy class provided by Polly which we will need to build.

Policies

In the case of a HttpClient, the common policies are the retry policy and the timeout policy. Here is an example of a WaitAndRetry policy.

1
2
3
4
5
HttpPolicyExtensions
    .HandleTransientHttpError()
    .OrResult(msg => msg.StatusCode == HttpStatusCode.NotFound)
    .Or<TimeoutRejectedException>()
    .WaitAndRetryAsync(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));

HttpPolicyExtensions.HandleTransientHttpError() returns a PolicyBuilder<HttpResponseMessage> provided by the Polly.Extensions.Http library. It setups a policy builder which handles 5.x.x and 408 error responses. We then add other type of messages to handle by using OrResult(T -> bool) where we add handling of 404 error responses. And lastly, we also handle timeout exceptions by using Or<Ttype> passing in the TimeoutRejectedException from Polly. Once the conditions are setup, we can apply the policy WaitAndRetryAsync where we retry for five times and wait in an exponential manner between each retry.

Timeout is easier as we only need to wait to a certain timespan:

1
Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(5));

And we are done implementing the two policies:

1
2
3
4
5
6
7
8
9
10
11
12
public static class PolicyHandler
{
    public static IAsyncPolicy<HttpResponseMessage> WaitAndRetry(int retryCount = 5) =>
        HttpPolicyExtensions
            .HandleTransientHttpError()
            .OrResult(msg => msg.StatusCode == HttpStatusCode.NotFound)
            .Or<TimeoutRejectedException>()
            .WaitAndRetryAsync(retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));

    public static IAsyncPolicy<HttpResponseMessage> Timeout(int seconds = 2) =>
        Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(seconds));
}

We can now simply add them using the .AddPolicyHanlder extension.

1
2
3
4
5
6
7
8
9
10
11
12
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    services
        .AddHttpClient<IApiClient, ApiClient>(opts =>
        {
            opts.BaseAddress = new Uri("http://localhost:5100");
        })
        .AddPolicyHandler(PolicyHandler.WaitAndRetry())
        .AddPolicyHandler(PolicyHandler.Timeout());
}

And this concludes today’s post, all calls from IMyApiClient.Get() will now be subject to the timeout and retry policies!

Conclusion

Today we saw how to implement timeout and retry policies for HttpClient in ASP NET Core using Polly. We started by defining a HttpClient service which would be used throughout our application. Next we moved on to install the Polly extensions for ASP NET Core and finished by implementing the timeout and retry policies. Hope you like this post, see you next time!

Designed, built and maintained by Kimserey Lam.