Use IHttpClientFactory and Polly(v8) to implement resilient HTTP requests
Recently, Polly team did fabulous job 🎉 by releasing the new version 8.0.0. It has lot of improvements and also they re-implemented Policy as Strategy. Polly v8 introduces the concept of resilience pipelines, a powerful tool that put together one or more resilience strategies.Here is the all changes you can find.
The Problem
Microsoft provides a extension Microsoft.Extensions.Http.Polly which allows us to use Polly with HttpClientFactory. It is very easy to use and you can find the documentation here.
But, there is a catch. as of today. the extension does not support Polly V8 yet. So, if you are using Polly V8, you can not use the extension.
The Solution
The solution is very simple. There are couple of options I can think of -
- Wait for the extension to support Polly V8. I am not sure when it will be available.
- Create our own extension to use Polly V8 with HttpClientFactory.
- Use the new extension Sundry.Extensions.Http.Polly which supports Polly V8 already! This is almost same as the Microsoft’s extension but it supports all new features of Polly V8.
In this post, I will be using the 3rd option. We will create a simple console application to demonstrate the usage of the extension. Let’s get started.
First, we will define a named or typed client HttpClient configuration in our standard Program.cs configuration. Now we will add incremental code specifying the resilience strategy we want to apply to the HttpClient. In this example, we will add a retry policy with exponential backoff.
1 |
|
We also need to add the nuget package Sundry.Extensions.Http.Polly to our project.
The differnce between the Microsoft’s extension and this extension is that, we need to add the AddResiliencePipelineHandler extension method to the HttpClientBuilder instead of AddPolicyHandler.
Let’s define our resilience strategy as well.
1 |
|
In this case, it’s adding a strategy for Http Retries with exponential backoff along with Jitter. This strategy will handle the HttpRequestException and HttpResponseMessage with status code other than 200. It will retry 5 times with 1 secondish delay between each retry. It will also log the retry attempt number and delay between each retry.
Sundry.Extensions.Http.Polly also provides built-in support for Transient Fault Handling Strategies. We will see the example below along with AddResiliencePipelineRegistry example.
In Polly v8 PolicyRegistry is replaced with ResiliencePipelineRegistry. Let’s see how we can use it with new extension.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
static IHostBuilder CreateHostBuilder(string[] args)
{
return Host
.CreateDefaultBuilder(args)
.ConfigureLogging((context, logging) =>
{
logging.SetMinimumLevel(LogLevel.Trace);
})
.ConfigureServices((context, services) =>
{
services.AddResiliencePipelineRegistry((_,y) => y.TryAddBuilder<HttpResponseMessage>("Retry", (builder, _) => builder.AddRetry(new RetryStrategyOptions<HttpResponseMessage>
{
ShouldHandle = HttpPolicyExtensions.HandleTransientHttpError(),
Delay = TimeSpan.FromSeconds(1),
MaxRetryAttempts = 5,
UseJitter = true,
BackoffType = DelayBackoffType.Exponential,
OnRetry = (args) =>
{
System.Console.WriteLine($"Using TryAddBuilder: Retry Attempt Number : {args.AttemptNumber} after {args.RetryDelay.TotalSeconds} seconds.");
return default;
},
})));
services
.AddHttpClient<IWeatherService, WeatherService>(client =>
{
client.BaseAddress = new Uri("http://localhost:5087/");
})
.AddResiliencePipelineHandlerFromRegistry("Retry");
});
}
In this case, We have registerd Retry strategy to the ResiliencePipelineRegistry with name Retry. This will create and cache resilience pipeline instances. We have also added the AddResiliencePipelineHandlerFromRegistry extension method to the HttpClientBuilder instead of AddResiliencePipelineRegistry to make use of the registered strategy.
Also, if you noticed, this example is using HttpPolicyExtensions.HandleTransientHttpError() to handle the transient errors.
It does support multiple overload which can be used to log using ILogger as well. Below is the example for the same.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static ResiliencePipeline<HttpResponseMessage> Retry(IServiceProvider sp)
{
return new ResiliencePipelineBuilder<HttpResponseMessage>()
.AddRetry(new RetryStrategyOptions<HttpResponseMessage>
{
ShouldHandle = HttpPolicyExtensions.HandleTransientHttpError(),
Delay = TimeSpan.FromSeconds(1),
MaxRetryAttempts = 5,
UseJitter = true,
BackoffType = DelayBackoffType.Exponential,
OnRetry = (args) =>
{
var logger = sp.GetRequiredService<ILogger>();
logger.LogTrace($"Retry Attempt Number : {args.AttemptNumber} after {args.RetryDelay.TotalSeconds} seconds.");
return default;
},
})
.Build();
}
Conclusion
I tried to keep the similar approach as Microsoft’s extension as close as possible. So that migration from v7 to v8 will be easy. Also kept the naming convention same as Polly v8. So, if you are using Polly v8, you can use this extension without any issue and you will get all the new features of Polly v8. Let me know if you have any feedback or suggestions. Also, if you want to contribute, you are more than welcome.
As always, You can find the source code here.