Using Retry Pattern In ASP.NET Core Via Polly

Background

 
When our applications need to communicate with many other services or components, we will meet temporary faults due to some of services or components that cannot respond in time.
 
There are many reasons for those temporary faults -- timeouts, overloaded resources, networking hiccups and so on. Most of them are hard to reproduce because these failures are usually self-correcting.
 
We can't avoid failures, but we can respond in ways that will keep our system up or at least minimize downtime.
 
For example, an application service with a throttling strategy is processing plenty of concurrent requests, and some of the requests will be rejected quickly. But if we can try again after a delay, it might succeed. Retry is an effective and easy way to handle transient failures.
 
And in this article, we will discuss it in ASP.NET Core via Polly.
 

What Is Polly?

Using Retry Pattern In ASP.NET Core Via Polly
 
Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner.
 

How To Use?

 
In the section, I will share two common scenarios, one is HTTP(s) request, the other one is sending a message to the message bus.
 

HTTP(s) request

 
Retry pattern with HTTP(s) request is very easy, because of the combination of Polly and HttpClientFactory.
 
What we need to do is use an extension method named AddPolicyHandler to add the retry policy for the HttpClient.
 
Here is the sample code to configure.
  1. public void ConfigureServices(IServiceCollection services)  
  2. {  
  3.     services.AddHttpClient("csharpcorner")  
  4.         .SetHandlerLifetime(TimeSpan.FromMinutes(5))  
  5.         // important step  
  6.         .AddPolicyHandler(GetRetryPolicy())  
  7.         ;  
  8.   
  9.     services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);  
  10. }  
  11.   
  12. private IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()  
  13. {  
  14.     return HttpPolicyExtensions  
  15.         // HttpRequestException, 5XX and 408  
  16.         .HandleTransientHttpError()  
  17.         // 404  
  18.         .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)  
  19.         // Retry two times after delay  
  20.         .WaitAndRetryAsync(2, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))  
  21.         ;  
  22. }  
GetRetryPolicy method means that when network failures or HTTP 5XX or 408 status codes will trigger retry mechanism.
 
To run the controller, we don't need to add some additional code before we send the HTTP(s) request, just keep the same behavior when we use HttpClient.
  1. [Route("api/[controller]")]  
  2. [ApiController]  
  3. public class ValuesController : ControllerBase  
  4. {  
  5.     private readonly IHttpClientFactory _clientFactory;  
  6.   
  7.     public ValuesController(IHttpClientFactory clientFactory)  
  8.     {  
  9.         _clientFactory = clientFactory;  
  10.     }  
  11.   
  12.     // GET api/values  
  13.     [HttpGet]  
  14.     public async Task<string> GetAsync()  
  15.     {  
  16.         // define a 404 page  
  17.         var url = $"https://www.c-sharpcorner.com/mytestpagefor404";  
  18.   
  19.         var client = _clientFactory.CreateClient("csharpcorner");              
  20.         var response = await  client.GetAsync(url);  
  21.         var result = await response.Content.ReadAsStringAsync();  
  22.         return result;  
  23.     }          
  24. }  
 After running up this project and visiting it, we may get the following logs.
 
Using Retry Pattern In ASP.NET Core Via Polly
 
As you can see, it sends three requests to the link; the last two are triggered by retry policy.
 

Sending a message to the message bus

 
There are many message buses we can choose for our applications. Here, we will use RabbitMQ to show how to do that.
 
When our applications communicate with RabbitMQ, we should deal with the retry mechanism via conventional usage of Polly.
  1. [Route("api/[controller]")]  
  2. [ApiController]  
  3. public class ValuesController : ControllerBase  
  4. {  
  5.     // GET api/values  
  6.     [HttpGet]  
  7.     public ActionResult<IEnumerable<string>> Get()  
  8.     {  
  9.         var message = Encoding.UTF8.GetBytes("hello, retry pattern");  
  10.   
  11.         var retry = Policy  
  12.             .Handle<Exception>()  
  13.             .WaitAndRetry(2, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));  
  14.   
  15.         try  
  16.         {  
  17.             retry.Execute(() =>  
  18.             {  
  19.                 Console.WriteLine($"begin at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}.");  
  20.                 var factory = new ConnectionFactory  
  21.                 {  
  22.                     HostName = "localhost",  
  23.                     UserName = "guest",  
  24.                     Password = "guest"  
  25.                 };  
  26.   
  27.                 var connection = factory.CreateConnection();  
  28.                 var model = connection.CreateModel();  
  29.                 model.ExchangeDeclare("retrypattern", ExchangeType.Topic, truefalsenull);  
  30.                 model.BasicPublish("retrypattern""retrypattern.#"falsenull, message);  
  31.             });  
  32.         }  
  33.         catch  
  34.         {  
  35.             Console.WriteLine("exception here.");  
  36.         }  
  37.   
  38.         return new string[] { "value1""value2" };  
  39.     }  
  40. }  
After running up this project and visiting it, we may get the following logs.
 
Using Retry Pattern In ASP.NET Core Via Polly
 

Summary

 
This article showed you two easy samples to use the retry pattern. Although it's very easy to use the retry pattern in our applications, we should consider where and when to use it but not everywhere. This is based on your business requirements.
 
I hope this article can help you!