Every call to an external service can fail: unstable network, overloaded server, timeouts. A robust application accounts for this from the start.
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.