All the applications often need to interact with various services and components to work correctly. Occasionally, these interactions might face temporary issues, such as timeouts, overloaded resources, or network glitches, because some services or components aren’t responding promptly. To handle these short-term problems, using retry and circuit-breaker patterns can be very effective. These methods are among the most common strategies for building resilient applications.
Retry in .NET
In .NET, “retry” essentially means “if something fails, try repeating the operation a set number of times before giving up.” For instance, if your service calls a third-party API and the call fails, the service will keep retrying until it successfully connects or reaches 10 attempts.
Circuit-breaker Pattern
In programming, a circuit-breaker pattern essentially means “if something goes wrong, stop trying to repeat the operation.” For example, if a database becomes overloaded, the circuit-breaker pattern would prevent your program from continuing to query it once the overload is detected.
Implementation
Implementing a retry pattern for HTTP(s) requests in .NET is straightforward. Developers just need to write an extension method called `AddPolicyHandler` to add the retry policy for the `HttpClient`. The following code snippet demonstrates this process. The `GetRetryPolicy` method will be called during network failures or when HTTP errors occur.
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("MysampleClient")
.SetHandlerLifetime(TimeSpan.FromMinutes(10))
.AddPolicyHandler(GetRetryPolicy());
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
private IAsyncPolicy GetRetryPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(3, retryAttempt)));
}
Next, the following code snippet demonstrates how to run the above code in a controller; the behavior will be the same when we use HttpClient:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private readonly IHttpClientFactory _clientFactory;
public ValuesController(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
[HttpGet]
public async Task GetAsync()
{
var url = $"https://www.mywebsite.com/homepage";
var client = _clientFactory.CreateClient("MysampleClient");
var response = await client.GetAsync(url);
var result = await response.Content.ReadAsStringAsync();
return result;
}
}
Pushing a Message in a Message Queue
The following .NET code below demonstrates how to push the message in a message queue:
Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
[HttpGet]
public ActionResult<IEnumerable> Get()
{
var message = Encoding.UTF8.GetBytes("My retry pattern");
var retry = Policy
.Handle()
.WaitAndRetry(2, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
try
{
retry.Execute(() =>
{
Console.WriteLine($"begin at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}.");
var factory = new ConnectionFactory
{
HostName = "localhost",
UserName = "Test",
Password = "Tes"
};
var connection = factory.CreateConnection();
var model = connection.CreateModel();
model.ExchangeDeclare("retrypattern", ExchangeType.Topic, true, false, null);
model.BasicPublish("retrypattern", "retrypattern.#", false, null, message);
});
}
catch
{
Console.WriteLine("exception here.");
}
return new string[] { "TestValue1", "TestValue2" };
}
}
Conclusion
In this article, we have learned about Polly circuit breaker policies and demonstrated how to use them with a .NET application that uses the HttpClientFactory pattern to create instances of HttpClient.