How to realize simple fuse degradation with httpclientfactory

Time:2019-10-29

Preface

After 2.1, there are many new things, including httpclientfactory. Httpclientfactory involves three or four kinds of clients, request middleware, integration with Polly, life cycle, etc.

After steeltoe’s components are upgraded to 2.1, many sample codes have already used httpclientfactory. Of course, it’s a digression.

This is mainly about the combination with Polly to complete simple fusing degradation. Before that, let’s look at the simplest use of httpclientfactory.

Simple use of httpclientfactory

Use a simple console program to demonstrate

Here is just to get the status code, not the actual content.


static async Task<string> BasicUsage()
{
  var serviceCollection = new ServiceCollection();
  serviceCollection.AddHttpClient();
  var services = serviceCollection.BuildServiceProvider();
  var clientFactory = services.GetService<IHttpClientFactory>();

  var client = clientFactory.CreateClient();
  var request = new HttpRequestMessage(HttpMethod.Get, "https://www.github.com");

  var response = await client.SendAsync(request).ConfigureAwait(false);

  return response.StatusCode.ToString();
}

In fact, the main operation is to add httpclient, and then create an httpclient object through httpclientfactory. With the httpclient object, the following operations should be needless to say.

Then call in the main method


Console.WriteLine($"BasicUsage, StatusCode = {BasicUsage().GetAwaiter().GetResult()}");

There is not much difference in the sense of usage. Let’s take a look at the combination with Polly.

Combination of httpclientfactory and Polly

Polly’s wiki page already has documentation for both.

https://github.com/App-vNext/Polly/wiki/Polly-and-HttpClientFactory

In fact, it’s very simple for us to use Polly’s features for HTTP requests.

We need to add nuget package of microsoft.extensions.http.polly when we use it.

Let’s look at three ways to extend Polly

Extension method Explain
AddTransientHttpErrorPolicy It mainly deals with HTTP request errors, such as HTTP 5xx status code, HTTP 408 status code and system.net.http.httprequestexception exception.
AddPolicyHandler Custom, consistent with the traditional way of defining Polly
AddPolicyHandlerFromRegistry Choose what you want from the policy collection (which is also customized).

The next operation is the addpolicyhandler used.

Because we want to achieve fuse degradation, we need to use circuit breakerpolicy and fallbackpolicy. At the same time, for the convenience of demonstration, we need to add a timeoutpolicy.

Because multiple policies are involved, we must determine their execution order!

Polly’s wiki page has an example with a very detailed sequence diagram.

In a word, it is the first one that works or the last one added.

Let’s create a new API project to demonstrate.

Modify the configureservices method as follows


public void ConfigureServices(IServiceCollection services)
{
  var fallbackResponse = new HttpResponseMessage();
  fallbackResponse.Content = new StringContent("fallback");
  fallbackResponse.StatusCode = System.Net.HttpStatusCode.TooManyRequests;

  services.AddHttpClient("cb", x =>
  {
    x.BaseAddress = new Uri("http://localhost:8000");
    x.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Test");
  })
  //fallback
  .AddPolicyHandler(Policy<HttpResponseMessage>.Handle<Exception>().FallbackAsync(fallbackResponse, async b =>
  {
    Logger.LogWarning($"fallback here {b.Exception.Message}");
  }))
  //circuit breaker
  .AddPolicyHandler(Policy<HttpResponseMessage>.Handle<Exception>().CircuitBreakerAsync(2, TimeSpan.FromSeconds(4), (ex, ts) =>
  {
    Logger.LogWarning($"break here {ts.TotalMilliseconds}");
  }, () =>
  {        
    Logger.LogWarning($"reset here ");
  }))
  //timeout
  .AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(1));
   
  services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

Then it is used in the controller.

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
  private static int myCount = 0;

  private readonly IHttpClientFactory _clientFactory;

  public ValuesController(IHttpClientFactory clientFactory)
  {
    this._clientFactory = clientFactory;
  }

  // GET api/values/timeout
  [HttpGet("timeout")]
  public ActionResult<IEnumerable<string>> Timeout()
  {
    If (MYCOUNT < 3) // simulation timeout
    {
      System.Threading.Thread.Sleep(3000);
    }
    myCount++;

    return new string[] { "value1", "value2" };
  }

  // GET api/values
  [HttpGet("")]
  public async Task<string> GetAsync()
  {
    var client = _clientFactory.CreateClient("cb");
    
    var request = new HttpRequestMessage(HttpMethod.Get, "/api/values/timeout");
    var response = await client.SendAsync(request);
    var content = await response.Content.ReadAsStringAsync();

    return content;
  }
}

The effect is as follows

For the previous requests, we will get the result of fallback because of timeout or fusing.

After 4 seconds, request again. Since there is no timeout, the result is obtained normally, so the fuse will be reset.

Take a look at the Journal

See all the operations clearly.

summary

Overall, httpclientfactory is very good. In particular, it can directly use Polly related features.

Part of the sample code: httpclientfactorydemo

Well, the above is the whole content of this article. I hope that the content of this article has some reference learning value for your study or work. If you have any questions, you can leave a message and exchange. Thank you for your support for developepaer.