.NET Core  

How to Use HttpClientFactory in .NET Core to Avoid Socket Exhaustion

Introduction

When building modern ASP.NET Core applications, making HTTP calls to external APIs is very common. Whether you are calling a payment gateway, third-party service, or another microservice, you often use HttpClient for sending requests.

However, many developers unknowingly misuse HttpClient, which can lead to a serious problem called socket exhaustion. This can cause your application to slow down or even crash under heavy load.

To solve this problem, .NET Core introduced IHttpClientFactory, a powerful feature that helps manage HttpClient instances efficiently.

In this article, you will learn what socket exhaustion is, why it happens, and how to use HttpClientFactory in .NET Core step by step with simple examples.

What is Socket Exhaustion?

Understanding Socket Exhaustion in Simple Words

Socket exhaustion happens when your application creates too many HTTP connections and does not release them properly.

Each HTTP request uses a network socket. If sockets are not reused or closed correctly, the system runs out of available sockets.

Why This Happens with HttpClient

Many developers write code like this:

public async Task<string> GetData()
{
    using (var client = new HttpClient())
    {
        return await client.GetStringAsync("https://api.example.com/data");
    }
}

This looks correct, but creating a new HttpClient for every request causes:

  • Too many open connections

  • Delayed socket release (TIME_WAIT state)

  • Resource exhaustion

This leads to performance issues in ASP.NET Core applications.

What is IHttpClientFactory in .NET Core?

Simple Definition

IHttpClientFactory is a built-in factory in .NET Core that helps you create and manage HttpClient instances efficiently.

It handles:

  • Connection pooling

  • DNS updates

  • Lifetime management

Benefits of IHttpClientFactory

  • Prevents socket exhaustion

  • Improves performance

  • Centralized configuration

  • Better testability

Step 1: Create ASP.NET Core Project

dotnet new webapi -n HttpClientFactoryDemo
cd HttpClientFactoryDemo

Step 2: Register HttpClientFactory

Open Program.cs and add:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

// Register HttpClientFactory
builder.Services.AddHttpClient();

var app = builder.Build();

app.UseHttpsRedirection();
app.MapControllers();
app.Run();

Step 3: Use IHttpClientFactory in Controller

Inject IHttpClientFactory

using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class DemoController : ControllerBase
{
    private readonly IHttpClientFactory _httpClientFactory;

    public DemoController(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    [HttpGet]
    public async Task<IActionResult> Get()
    {
        var client = _httpClientFactory.CreateClient();

        var response = await client.GetAsync("https://jsonplaceholder.typicode.com/posts");

        var data = await response.Content.ReadAsStringAsync();

        return Ok(data);
    }
}

Now HttpClient instances are managed efficiently.

Step 4: Named Clients

What are Named Clients?

Named clients allow you to configure different HttpClient instances for different APIs.

Configure Named Client

builder.Services.AddHttpClient("MyApi", client =>
{
    client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
    client.Timeout = TimeSpan.FromSeconds(10);
});

Use Named Client

var client = _httpClientFactory.CreateClient("MyApi");
var response = await client.GetAsync("posts");

Step 5: Typed Clients

What are Typed Clients?

Typed clients provide a clean and strongly-typed way to use HttpClient.

Create Typed Client

public class MyApiService
{
    private readonly HttpClient _httpClient;

    public MyApiService(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<string> GetPosts()
    {
        return await _httpClient.GetStringAsync("posts");
    }
}

Register Typed Client

builder.Services.AddHttpClient<MyApiService>(client =>
{
    client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
});

Use Typed Client

public class DemoController : ControllerBase
{
    private readonly MyApiService _service;

    public DemoController(MyApiService service)
    {
        _service = service;
    }

    [HttpGet]
    public async Task<IActionResult> Get()
    {
        var data = await _service.GetPosts();
        return Ok(data);
    }
}

Step 6: Configure HttpClient Handler Lifetime

To avoid DNS issues and reuse connections efficiently:

builder.Services.AddHttpClient("MyApi")
    .SetHandlerLifetime(TimeSpan.FromMinutes(5));

This ensures connections are refreshed periodically.

Step 7: Add Polly for Resilience

You can add retry policies using Polly:

builder.Services.AddHttpClient("MyApi")
    .AddTransientHttpErrorPolicy(policy =>
        policy.WaitAndRetryAsync(3, _ => TimeSpan.FromSeconds(2)));

This improves reliability in real-world applications.

Step 8: Best Practices

Follow These Best Practices

  • Always use IHttpClientFactory

  • Avoid creating HttpClient manually

  • Use named or typed clients

  • Configure timeouts

  • Add retry policies

Real-World Example

In a microservices architecture:

  • Service A calls Service B using HttpClientFactory

  • Connections are reused

  • Failures are handled with retries

This improves scalability and performance.

Summary

HttpClientFactory in .NET Core helps prevent socket exhaustion by managing HTTP connections efficiently through connection pooling and proper lifecycle management. Instead of creating multiple HttpClient instances, developers can use IHttpClientFactory to reuse connections, improve performance, and build scalable ASP.NET Core applications. Using named clients, typed clients, and resilience policies further enhances reliability and maintainability.