An application might need to send or get data from another system using provided APIs. However, Http Request from within an application is difficult to control. Issues may arise when the application sends a request to the target system. Sometimes, the network has a problem, the target system is not responding, or the target database is slow. Those challenges may cause unhandled errors and break the application.
To resolve that problem and improve resiliency, an application should safely send Http Request and receive Http Response. It should also retry or break the operation when there is a problem with the Http Request sent to the target server.
In .Net, developers can implement IHttpClientFactory and Polly to improve the resiliency of an application, especially when they need to handle Http Request and Http Response in their code. This article will explain what IHttpClientFactory and Polly is and give an overview of implementing it on a project.
IHttpClientFactory is a new feature introduced in .Net Core 2.1. Before it was introduced, .Net Framework provided IHttpClient class when developers need to handle Http Request in their code. Http Client is a base class for sending Http Request and receiving Http Response from a resource identified by a URI. Though this class can be easily used, it is not correctly used in some cases and potentially raises issues like the Socket Exception problem.
IHttpClientFactory is introduced to address those issues and to make IHttpClient instances manageable. It comes with an interface, IHttpClientFactory, which can create and configure HttpClient instances through Dependency Injection.
The implementation of IHttpClientFactory provides a central location for naming and configuring HttpClient object. In addition, it provides extensions for Polly-based middleware that can be used to delegate Http Request handler. Developers can register HTTP clients into the factory and use Polly handler for Retry or Circuit Breaker, improving application resiliency.
Polly itself is a fault-handling library provided for the .Net application that allows developers to implement Retry, Timeout, and Circuit Breaker policies in their code. As mentioned above, Polly can be applied along with implementing IHttpClientFactory to improve the application's durability.
There are several ways to use IHttpClientFactory in an application:
- Basic Usage
- Use Named Client
- Use Typed Client
- Use Generated Client
Typed Clients or Service Agent pattern is the most structured way to use IHttpClientFactory, which Microsoft also recommends for consuming HttpClient. Also, Typed Clients gives the benefit of encapsulating service into a single class.
To configure IHttpClientFactory using Typed Clients, first, we must install Microsoft.Extensions.Http NuGet package into the application. Then we can register Typed Clients that need to use HttpClient using the AddHttpClient() extension method inside the standard Startup.ConfigureServices(…) method.
In this code, we register one Typed Client or service that will be used to handle the HttpRequest and HttpResponse when we need to communicate with other applications. Registering the client services as above will create a standard HttpClient for the service.
SetHandlerLifetime() method is used to set the lifetime of the HttpMessageHandler objects. This lifetime is the length of time that an HttpMessageHanlder instance can be reused in the pool. It has two minutes as its default value, but it can be overridden per Typed Client.
We also need to implement our Typed Client class that uses the injected and configured HttpClient. In this case, we define a HttpService that will be referenced by other services to handle remote Http calls. Our HttpService class is defined with two primary methods for sending Http Post Request, and Http Get Request.
Once we have the typed class implemented and configured in the Startup class, we can use it in another service like in the following code:
With the codes above, our application or system sends a request to a target endpoint. We also add fundamental error handling to ensure that it can continue the operation despite any failure response from the call.
As stated above, we can improve the implementation of HttpClient. Not only can we handle the error from a failure response, but if there is a specific response from the target endpoint using Polly, we can define what our system will do.
Below is an example of how to implement Polly and use some of its resiliency policy.
First, we need to install Polly via NuGet, just like IHttpClientFactory. Then add Microsoft.Extensions.Http.Polly as a reference in the Startup class.
We use Polly by adding incremental code specifying the policy in the HttpClient configuration.
The AddPolicyHandler() method is what we use to add the policy to the HttpClient object used in the application. In the code above, we add retry policies for each Http Get and Http Post method, along with a circuit breaker policy.
GetRetryPolicy(), PostRetryPolicy(), and CircuitBreakerPolicy() methods are Polly policies that are defined as separate methods.
In the above example, we first define a static class specifying some Http status codes worth retrying, such as Bad Gateway, Service Unavailable, and Gateway Timeout. We need to pay attention to the status code we can retry and when we cannot retry the request. The codes written in the example are the status codes that usually mean that the target machine or system is temporarily unavailable, so it is safe to assume that we can retry our request when we get these responses. On the contrary, most of 4** responses should not be retried since the client's error causes them, for example, wrong parameters, bad requests, and wrong authorization. 5** errors are not worth retrying, such as 500 Internal Server Error and 501 Not Implemented.
In the GetRetryPolicy() and PostRetryPolicy() methods we define the number of retries for each policy. The example provides a simple configuration for retrying three times every three minutes. Sometimes it is also recommended that we use exponential wait time for the retry so that the server has more time to recover or stabilize before trying again. Polly has two helpers that can be used to handle the scenario.
The first helper is the LinearBackoff helper. We can use this helper to define a linearly increasing retry delay. For example:
This will create a linear retry delay of 1000, 2000, 3000, 4000, 5000 seconds.
The other one is the ExponentialBackoff helper that can be used to define an exponentially increasing retry delay. The code below is how we create an exponentially growing retry delay of 1000, 2000, 4000, 8000, 16000 seconds.
Lastly, we also add the CircuitBreakerPolicy() method. The code in this method is used to break the circuit for 30 seconds if more than 50% action resulted in handled exception within the 1-minute duration. Circuit Breaker policy in Polly will cut the connection from our application to a faulting system, just like how a real-world in-house circuit breaker cuts electricity to an outlet. If this scenario happens, any action placed for execution through the policy will not be executed, and the call will fail. The circuit will be cut for the configured duration. After that, the following action will be treated as a trial to determine the circuit's health and if the application can send any request to the server.
In summary, the implementation of IHttpClientFactory and Polly, as shown above, can improve the resiliency of an application when it needs to communicate with another service or system via provided URI or API. IHttpClientFactory can simplify the configuration required for safely sending Http Request and receiving Http Response, and Polly adds the ability for HttpClient to retry the request or even break the circuit when there is an error in communicating with a remote server.
Andreanus Agung Purnomo - Analyst Programmer