Wrapping the circuit breaker with retry

So far, we have learned how circuit breaker and retry patterns can be used and implemented using the Polly framework. The retry pattern is used to retry the request if it fails for a specified amount of time, where the circuit breaker keeps the state of the circuit and, based on the threshold of the requests being failed, makes the circuit open and stops calling the remote service for some time, as specified in the configuration to save network bandwidth.

With the Polly framework, we can use the retry and circuit breaker patterns in conjunction and wrap the circuit breaker with the retry pattern to open the circuit if the retry pattern reaches the count of the failed request threshold limit.

In this section, we will develop a custom HttpClient class that provides methods such as GET, POST, PUT, and DELETE, and use retry and circuit breaker policies to make it resilient.

Create a new IResilientHttpClient interface and add four methods for HTTP GET, POST, PUT, and DELETE:

public interface IResilientHttpClient 
{ 
  HttpResponseMessage Get(string uri); 
 
  HttpResponseMessage Post<T>(string uri, T item); 
 
  HttpResponseMessage Delete(string uri); 
 
  HttpResponseMessage Put<T>(string uri, T item); 
} 

Now, create a new class called ResilientHttpClient, which implements the IResilientHttpClient interface. We will add a parameterized constructor to inject the circuit breaker policy and a HttpClient object, which will be used to make HTTP GET, POST, PUT, and DELETE requests. Here is the constructor implementation of the ResilientHttpClient class:

public class ResilientHttpClient : IResilientHttpClient 
{ 
         
  static CircuitBreakerPolicy<HttpResponseMessage> _circuitBreakerPolicy; 
  static Policy<HttpResponseMessage> _retryPolicy; 
  HttpClient _client; 
         
  public ResilientHttpClient(HttpClient client, 
CircuitBreakerPolicy<HttpResponseMessage> circuitBreakerPolicy) { _client = client; _client.DefaultRequestHeaders.Accept.Clear(); _client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json")); //circuit breaker policy injected as defined in the Startup class _circuitBreakerPolicy = circuitBreakerPolicy; //Defining retry policy _retryPolicy = Policy.HandleResult<HttpResponseMessage>(x => { var result = !x.IsSuccessStatusCode; return result; })
//Retry 3 times and for each retry wait for 3 seconds .WaitAndRetry(3, sleepDuration => TimeSpan.FromSeconds(3)); } }

In the preceding code, we have defined the CircuitBreakerPolicy<HttpResponseMessage> and HttpClient objects, which are injected through DI. We have defined the retry policy and set the retry threshold to three times, where each retry will wait for three seconds before making a call to the service.

Next, we will create the ExecuteWithRetryandCircuitBreaker method, which takes a URI and a delegate function that will be executed within the retry and circuit breaker policies. Here is the code snippet of the ExecuteWithRetryandCircuitBreaker method:

//Wrap function body in Retry and Circuit breaker policies 
public HttpResponseMessage ExecuteWithRetryandCircuitBreaker(string uri, Func<HttpResponseMessage> func) 
{ 
 
  var res = _retryPolicy.Wrap(_circuitBreakerPolicy).Execute(() => func()); 
  return res; 
} 

We will call this method from our GET, POST, PUT, and DELETE implementation and define the code that will be executed within the retry and circuit breaker policies.

Here is the implementation for the GET, POST, PUT, and DELETE methods, respectively:

public HttpResponseMessage Get(string uri) 
{ 
  //Invoke ExecuteWithRetryandCircuitBreaker method that wraps the code 
//with retry and circuit breaker policies return ExecuteWithRetryandCircuitBreaker(uri, () => { try { var requestMessage = new HttpRequestMessage(HttpMethod.Get, uri); var response = _client.SendAsync(requestMessage).Result; return response; }
catch(Exception ex) { //Handle exception and return InternalServerError as response code HttpResponseMessage res = new HttpResponseMessage(); res.StatusCode = HttpStatusCode.InternalServerError; return res; } }); } //To do HTTP POST request public HttpResponseMessage Post<T>(string uri, T item) { //Invoke ExecuteWithRetryandCircuitBreaker method that wraps the code
//with retry and circuit breaker policies return ExecuteWithRetryandCircuitBreaker(uri, () => { try { var requestMessage = new HttpRequestMessage(HttpMethod.Post, uri); requestMessage.Content = new StringContent(JsonConvert.SerializeObject(item),
System.Text.Encoding.UTF8, "application/json"); var response = _client.SendAsync(requestMessage).Result; return response; }catch (Exception ex) { //Handle exception and return InternalServerError as response code HttpResponseMessage res = new HttpResponseMessage(); res.StatusCode = HttpStatusCode.InternalServerError; return res; } }); } //To do HTTP PUT request public HttpResponseMessage Put<T>(string uri, T item) { //Invoke ExecuteWithRetryandCircuitBreaker method that wraps
//the code with retry and circuit breaker policies return ExecuteWithRetryandCircuitBreaker(uri, () => { try { var requestMessage = new HttpRequestMessage(HttpMethod.Put, uri); requestMessage.Content = new StringContent(JsonConvert.SerializeObject(item),
System.Text.Encoding.UTF8, "application/json"); var response = _client.SendAsync(requestMessage).Result; return response; } catch (Exception ex) { //Handle exception and return InternalServerError as response code HttpResponseMessage res = new HttpResponseMessage(); res.StatusCode = HttpStatusCode.InternalServerError; return res; } }); } //To do HTTP DELETE request public HttpResponseMessage Delete(string uri) { //Invoke ExecuteWithRetryandCircuitBreaker method that wraps the code
//with retry and circuit breaker policies return ExecuteWithRetryandCircuitBreaker(uri, () => { try { var requestMessage = new HttpRequestMessage(HttpMethod.Delete, uri); var response = _client.SendAsync(requestMessage).Result; return response; } catch (Exception ex) { //Handle exception and return InternalServerError as response code HttpResponseMessage res = new HttpResponseMessage(); res.StatusCode = HttpStatusCode.InternalServerError; return res; } }); }

Finally, in our startup class, we will add the dependencies as follows:

public void ConfigureServices(IServiceCollection services) 
{ 
 
  var circuitBreakerPolicy = Policy.HandleResult<HttpResponseMessage>(x=> { 
    var result = !x.IsSuccessStatusCode; 
    return result; 
  }) 
  .CircuitBreaker(3, TimeSpan.FromSeconds(60), OnBreak, OnReset, OnHalfOpen); 
 
   services.AddSingleton<HttpClient>(); 
   services.AddSingleton<CircuitBreakerPolicy<HttpResponseMessage>>(circuitBreakerPolicy); 
             
   services.AddSingleton<IResilientHttpClient, ResilientHttpClient>(); 
   services.AddMvc(); 
   services.AddSwaggerGen(c => 
   { 
     c.SwaggerDoc("v1", new Info { Title = "User Service", Version = "v1" }); 
   }); 
 } 

In our UserController class, we can inject our custom ResilientHttpClient object through DI and modify the POST method, which is shown as follows:

[Route("api/[controller]")] 
public class UserController : Controller 
{ 
 
  IResilientHttpClient _resilientClient; 
 
  HttpClient _client; 
  CircuitBreakerPolicy<HttpResponseMessage> _circuitBreakerPolicy; 
  public UserController(HttpClient client, IResilientHttpClient resilientClient) 
  { 
    _client = client; 
    _resilientClient = resilientClient; 
 
  } 
 
  // POST api/values 
  [HttpPost] 
  public async Task<IActionResult> Post([FromBody]User user) 
  { 
 
    //Email service URL 
    string emailService = "http://localhost:80/api/Email"; 
 
    var response = _resilientClient.Post(emailService, user); 
    if (response.IsSuccessStatusCode) 
    { 
      var result = response.Content.ReadAsStringAsync(); 
      return Ok(result); 
    } 
 
    return StatusCode((int)response.StatusCode, response.Content.ReadAsStringAsync()); 
            
  } 
} 

With this implementation, the circuit will be initially closed when the application starts. When the request is made to the EmailService, if the service does not respond, it will try to call the service three times, waiting for three seconds on each request. If the service doesn't respond, the circuit will become open and for all subsequent requests, will stop calling the email service and will return the exception to the user for 60 seconds, as specified in the circuit breaker policy. After 60 seconds, the next request will be made to the EmailService and the circuit breaker state will be changed to Half-open. If it responds, the circuit state becomes closed again; otherwise, it remains in an open state for the next 60 seconds.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.190.156.212