Wednesday, February 12, 2020

Microservices – Creating Resilient Services


Dealing with unexpected failures is one of the hardest problems to solve, especially in a distributed system.

A microservice needs to be resilient to failures, which means that it needs to be able to restart when a failure happens.

In the microservice environment there are a lot of communication between services and sometimes a service may not be responding, which can be a problem to the services that are trying to communicate with the non-responsive service.

Let`s see how to implement two design patters that can help us to make a Microservice Resilient.

Exponential Back off Pattern         

Sometimes non-responsive Microservices are transient and they get fixed after a short delay.
There are several problems that can cause a Microservice to stop responding, for example: Network issues, timeout…

When it happens all we have to do is wait a little bit and try to call it again.
The Exponential Back off Pattern will help us achieve that, every time that an api call fails the pattern will exponentially wait and try again.

Let`s suppose that a call to an api failed for the first time, the system will wait 2 seconds (2s pow 1) and try again;

If it fails again it will wait 4 seconds (2s pow 2);

If it fails again it will wait 8 seconds (2s pow 3);

The formula is pretty simple:

Wait Time = Seconds pow Number of Attempt.


Why should we wait exponentially?

If a Microservice is too busy handling too many requests, send more requests won`t help at all.

Waiting exponentially will help preventing socket exhaustion.

Should keep trying to call the Microservice forever?

No, we try a couple of times and if it still doesn`t work then the application should handle the error.

Circuit Breaker Pattern

When the error is not transient and it`s not gonna get fixed anytime soon we need to immediately fail all the api calls to that service and return an error warning that it`s not available.

This pattern will prevent the system do make more requests to a non-responsive Microservice to avoid socket exhaustion and give a break to the Microservice that is non-responsive.

This pattern will open a Circuit for a certain amount of time and any call to the non-responsive Microservice while the circuit is open will automatically return an error. 

Implementing the Patterns



Create an ASP.NET Core Web Application

















Change the framework version to 3.0



Install the package Microsoft.Extensions.Http.Polly










Create an interface that will represent the HttpClient class that will make calls to other service.

namespace MicroservicePatterns
{
    public interface IExampleClient
    {
        Task<string> GetCountryName(string countryCode);
    }
}

Create a class that will implement the interface

namespace MicroservicePatterns
{
    public class ExampleClient : IExampleClient
    {
        private readonly HttpClient client;

        public ExampleClient(HttpClient client)
        {
            this.client = client;
            client.BaseAddress = new Uri("http://microservice-url/api/Country");
            client.DefaultRequestHeaders.Add("content-type", "application/json");
        }
        public async Task<string> GetCountryName(string countryCode)
        {
            var response = await client.GetAsync($"{client.BaseAddress}/{countryCode}").ConfigureAwait(false);
            var responseData = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
            return responseData;
        }
    }
}
Note that we are not creating new instances of the HttpClient class, it will be created by the HttpClientFactory and injected to our class.

Let`s setup the HttpClientFactory in the startup class. 

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.AddHttpClient<IExampleClient, ExampleClient>();
        }

This will ensure that the HttpClient will be created by the .net core framework and injected into our class.
In order to make the patterns work we need to use let the framework manage the instances for, never create an instance of the HttpClient class manually.

Let`s create the implementation of the Exponential Back off pattern

In the startup class create the method:

private IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
        {
            return HttpPolicyExtensions.HandleTransientHttpError().OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
                .WaitAndRetryAsync(retryCount: 3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
        }

When we implement the back off pattern using Polly we need to filter the status codes that we are going to apply the pattern.

In the example above everytime the application get a NotFound status code as result of a call a new attempt will be made.

The max number of attempts is set to 3 and for each attempt we are waiting exponentially, in the example we are doing 2 seconds pow Attempt.

Let`s create the implementation of the Circuit Breaker pattern

private IAsyncPolicy<HttpResponseMessage> GetCicuitBreaker()
        {
            return HttpPolicyExtensions.HandleTransientHttpError().CircuitBreakerAsync(3, TimeSpan.FromSeconds(30));
        }

In the example above we created a policy to open a circuit that will automatically fails all the requests during 30 seconds after 3 failed attempts.

Now let`s add the policies we created to our client factory

We already have registered the factory to the ExampleClient class then all we have to do is add the policies to it, see below:

services.AddHttpClient<IExampleClient, ExampleClient>()
                .AddPolicyHandler(GetRetryPolicy())
                .AddPolicyHandler(GetCicuitBreakerPolicy());
  

Conclusion


The Circuit Breaker and Back off patterns are excellent to improve resilience and they work together, while Back off will retry a service call a couple of times the other will prevent more calls for a certain period of time after those retries to prevent problems like socket exhaustion.

Microservices – Creating Resilient Services

Dealing with unexpected failures is one of the hardest problems to solve, especially in a distributed system . A microservice needs to...