Case 10: The Socket Exhaustion Trap - Misusing HttpClient
The Story
Imagine your ASP.NET Core application needs to call other APIs to get data. For example, it might call an inventory service to check if a product is in stock.
The application works perfectly on your computer and in testing. But when it goes live and many users start using it, the application suddenly starts failing with a strange error: SocketException.
The Problem Code (It Looks Correct)
Because HttpClient is IDisposable, many developers think they should create and destroy it for every single API call, using a using block. This is how we are taught to handle IDisposable objects like DbContext.
public class InventoryService
{
public async Task<int> GetStockCountAsync(int productId)
{
// THE CLASSIC MISTAKE: Creating a new HttpClient for every request.
using (var client = new HttpClient())
{
var response = await client.GetStringAsync($"https://api.inventory.com/stock/{productId}");
return int.Parse(response);
}
}
}The Real Danger: Socket Exhaustion
A Misunderstanding:
HttpClientis designed to be reused. It is safe to share oneHttpClientfor many requests at the same time (it's thread-safe).The Real Problem:
- When you create a new
HttpClient, it takes a network socket from the operating system to make the connection. - When the
usingblock ends, theHttpClientis disposed, but the socket is not released immediately. It goes into aTIME_WAITstate for a few minutes to make sure all data was sent correctly. - Under high load: Your application creates and disposes
HttpClients very quickly. This uses up all the available sockets on the server. This is called socket exhaustion. - When there are no more sockets, your application cannot make any more outgoing HTTP calls. It can't talk to other services, and it effectively crashes.
- When you create a new
The Solution: Use IHttpClientFactory
ASP.NET Core provides a built-in service called IHttpClientFactory to solve this exact problem. It manages a pool of HttpClient handlers, allowing sockets to be reused safely and efficiently.
Here is the better code:
Register it in
Program.cs:csharpbuilder.Services.AddHttpClient();Inject and use the factory:
csharppublic class InventoryService { private readonly IHttpClientFactory _httpClientFactory; public InventoryService(IHttpClientFactory httpClientFactory) { _httpClientFactory = httpClientFactory; } public async Task<int> GetStockCountAsync(int productId) { // Get a managed HttpClient from the factory var client = _httpClientFactory.CreateClient(); var response = await client.GetStringAsync($"https://api.inventory.com/stock/{productId}"); return int.Parse(response); } }
What We Gained
- No More Socket Exhaustion: The underlying network sockets are reused efficiently. Your application can handle thousands of outgoing HTTP requests without problems.
- Centralized Management:
IHttpClientFactorygives you one place to set up policies for all yourHttpClients, such as timeouts, automatic retries, and more advanced patterns like circuit breakers.
The Golden Rules
- Never create a new
HttpClientin ausingblock for each request. - Always use
IHttpClientFactoryto create and manageHttpClientinstances in modern ASP.NET Core applications.
Misusing HttpClient is one of the most serious technical mistakes that can bring down a production system.