Last active
August 21, 2021 14:16
-
-
Save yetanotherchris/389b17b5495c08384d0def5185d740a2 to your computer and use it in GitHub Desktop.
Polly CircuitBreaker, Retry and Timeout configuration example for HttpClient and HttpClientFactory
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.AspNetCore.Mvc; | |
using Microsoft.Extensions.Logging; | |
using System; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.Linq; | |
using System.Net.Http; | |
using System.Threading.Tasks; | |
using TestCircuitBreaker.Models; | |
namespace TestCircuitBreaker.Controllers | |
{ | |
public class HomeController : Controller | |
{ | |
private readonly ILogger<HomeController> _logger; | |
private readonly IHttpClientFactory _factory; | |
public HomeController(ILogger<HomeController> logger, IHttpClientFactory factory) | |
{ | |
_logger = logger; | |
_factory = factory; | |
} | |
public async Task<IActionResult> Index() | |
{ | |
var client = _factory.CreateClient("localhost509"); | |
string result = await client.GetStringAsync("/"); | |
return Content(result); | |
} | |
public IActionResult Privacy() | |
{ | |
return View(); | |
} | |
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] | |
public IActionResult Error() | |
{ | |
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.AspNetCore.Builder; | |
using Microsoft.AspNetCore.Hosting; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.AspNetCore.HttpsPolicy; | |
using Microsoft.Extensions.Configuration; | |
using Microsoft.Extensions.DependencyInjection; | |
using Microsoft.Extensions.Hosting; | |
using Polly; | |
using Polly.Extensions.Http; | |
using Polly.Timeout; | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Net.Http; | |
using System.Threading.Tasks; | |
// dotnet add package Polly.Extensions.Http | |
// dotnet add package Microsoft.Extensions.Http.Polly | |
// see: https://github.com/App-vNext/Polly/wiki/Polly-and-HttpClientFactory | |
namespace TestCircuitBreaker | |
{ | |
public class Startup | |
{ | |
public Startup(IConfiguration configuration) | |
{ | |
Configuration = configuration; | |
} | |
public IConfiguration Configuration { get; } | |
// This method gets called by the runtime. Use this method to add services to the container. | |
public void ConfigureServices(IServiceCollection services) | |
{ | |
services.AddLogging(); | |
var retryPolicy = HttpPolicyExtensions | |
.HandleTransientHttpError() | |
.Or<TimeoutRejectedException>() // thrown by Polly's TimeoutPolicy if the inner execution times out | |
.RetryAsync(15); | |
var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(1); | |
var circuitPolicy = HttpPolicyExtensions | |
.HandleTransientHttpError() | |
.Or<TimeoutRejectedException>() | |
.CircuitBreakerAsync( | |
handledEventsAllowedBeforeBreaking: 5, | |
durationOfBreak: TimeSpan.FromSeconds(20), | |
onBreak: (response, timeSpan) => | |
{ | |
Console.Write("broken"); | |
}, | |
onReset: () => | |
{ | |
Console.WriteLine("reset"); | |
}); | |
// Retry executes Circuit (if the circuit is open, it fails), which executes Timeout | |
services | |
.AddHttpClient("localhost509", c => c.BaseAddress = new Uri("http://localhost:509")) | |
.AddPolicyHandler(retryPolicy) | |
.AddPolicyHandler(circuitPolicy) | |
.AddPolicyHandler(timeoutPolicy); | |
services.AddControllersWithViews(); | |
} | |
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. | |
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) | |
{ | |
app.UseHttpsRedirection(); | |
app.UseStaticFiles(); | |
app.UseMiddleware<ExceptionMiddleware>(); | |
app.UseRouting(); | |
app.UseAuthorization(); | |
app.UseEndpoints(endpoints => | |
{ | |
endpoints.MapControllerRoute( | |
name: "default", | |
pattern: "{controller=Home}/{action=Index}/{id?}"); | |
}); | |
} | |
} | |
public class ExceptionMiddleware | |
{ | |
private readonly RequestDelegate _next; | |
public ExceptionMiddleware(RequestDelegate next) | |
{ | |
_next = next; | |
} | |
public async Task Invoke(HttpContext httpContext) | |
{ | |
try | |
{ | |
await _next(httpContext); | |
} | |
catch (Exception ex) | |
{ | |
Console.WriteLine($"================ {ex.GetType().ToString()}"); | |
httpContext.Response.StatusCode = StatusCodes.Status226IMUsed; // something to distinguish this middleware | |
await httpContext.Response.WriteAsJsonAsync("{ 'a' : 1 }"); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment