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:
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
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:
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.