loading experience

.NET

Resilient HttpClient in .NET: retry, timeout and circuit breaker

Calling external services without bringing down your application when they slow down or fail.

Resilient HttpClient in .NET: retry, timeout and circuit breaker

Every call to an external service can fail: unstable network, overloaded server, timeouts. A robust application accounts for this from the start.

Every call goes through the layers in order, from outside in.
Every call goes through the layers in order, from outside in.

IHttpClientFactory

Creating a new HttpClient for every call exhausts connections; keeping a single one forever ignores DNS changes. The factory handles both and is the recommended approach.

builder.Services.AddHttpClient<WeatherClient>(c =>
{
    c.BaseAddress = new Uri("https://api.weather.example/");
    c.Timeout = TimeSpan.FromSeconds(30);
})
.AddStandardResilienceHandler();

The standard resilience handler

The Microsoft.Extensions.Http.Resilience package adds, in one line, a well-designed pipeline: concurrency limiter, total timeout, retry with increasing randomised delay, circuit breaker and per-attempt timeout. The values can be tuned:

.AddStandardResilienceHandler(o =>
{
    o.Retry.MaxRetryAttempts = 3;
    o.AttemptTimeout.Timeout = TimeSpan.FromSeconds(5);
    o.TotalRequestTimeout.Timeout = TimeSpan.FromSeconds(20);
});

The typed client

public class WeatherClient(HttpClient http)
{
    public Task<Forecast?> TodayAsync(string city, CancellationToken ct) =>
        http.GetFromJsonAsync<Forecast>($"forecasts/{Uri.EscapeDataString(city)}", ct);
}

Be careful with retries

Repeating a read is safe; repeating a payment is not. Operations that modify data should only be retried if they are idempotent, for example thanks to an idempotency key sent to the server. And when the external service is down for a long time, the circuit breaker stops hammering it and returns a manageable error immediately.

Comments (0)

No comments yet.

Leave a comment