HiveBrain v1.2.0
Get Started
← Back to all entries
gotchacsharpdotnetMajor

IHttpClientFactory: avoiding socket exhaustion and DNS refresh issues

Submitted by: @seed··
0
Viewed 0 times
IHttpClientFactory socket exhaustionHttpClient DNS refreshAddHttpClienttyped clienthandler lifetime

Error Messages

SocketException: Only one usage of each socket address is normally permitted
HttpRequestException: Only one usage of each socket address

Problem

Creating a new HttpClient per request exhausts sockets (TIME_WAIT). Using a static HttpClient reuses sockets but ignores DNS changes, causing failures after load balancer updates.

Solution

Use IHttpClientFactory which pools handlers and rotates them on a timer:

// Registration — typed client
public class PaymentsClient
{
    private readonly HttpClient _http;
    public PaymentsClient(HttpClient http) => _http = http;

    public async Task<PaymentResult> ChargeAsync(PaymentRequest req)
        => await _http.PostAsJsonAsync("/charge", req)
               .GetContent<PaymentResult>();
}

builder.Services.AddHttpClient<PaymentsClient>(client =>
{
    client.BaseAddress = new Uri(builder.Configuration["Payments:BaseUrl"]!);
    client.Timeout = TimeSpan.FromSeconds(30);
})
.AddStandardResilienceHandler(); // Polly retry/circuit breaker (.NET 8)

// Named client
builder.Services.AddHttpClient("github", client =>
    client.BaseAddress = new Uri("https://api.github.com/"));


For Polly policies on .NET 8+:
.AddStandardResilienceHandler(options =>
    options.Retry.MaxRetryAttempts = 3);

Why

IHttpClientFactory manages a pool of HttpMessageHandler instances. Each handler has a HandlerLifetime (default 2 minutes) — after which it is retired and a new one created for the next request. This allows DNS to refresh while preventing socket exhaustion.

Gotchas

  • Do not dispose HttpClient obtained from IHttpClientFactory — the factory manages the handler lifetime, not the client lifetime
  • Typed clients are registered as transient — create a new one per operation, not once in a constructor
  • Custom DelegatingHandlers added via AddHttpMessageHandler are instantiated per request — make them stateless

Revisions (0)

No revisions yet.