Performance Boost from Reusing HttpClient Connections

By Tan Lee Published on Jan 21, 2025  178
When you reuse an HttpClient instance for multiple requests, the connection is kept open and reused for each subsequent request.

Reusing connections can boost performance in the following scenarios:

  • Sending multiple sequential requests to the same endpoint.
  • Sending concurrent requests to the same endpoint.

To demonstrate the performance improvement, I'll send multiple requests to a URL.

The first request will require opening a new connection, while the following requests will reuse the established connection. I’ll measure the response times for both cases.

It's important to discard the first measurement (due to warm-up time), but for this scenario, I’ll perform a warm-up request to a different URL (localhost) before beginning the actual performance test.

Below is a C# code sample that measures the time taken to open and reuse a connection using HttpClient:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public static async Task MeasureConnectionReuse()
{
    var httpClient = new HttpClient();
 
    // Warm up the framework with a request to localhost
    await SendRequest(httpClient, "https://localhost:9000/api/warmup");
 
    Stopwatch stopwatch = Stopwatch.StartNew();
    await SendRequest(httpClient, apiUrl);
    stopwatch.Stop();
    Console.WriteLine($"Time to open a new connection: {stopwatch.ElapsedMilliseconds}ms");
 
    List<double> responseTimes = new List<double>();
 
    // Measure response times for reusing the connection
    for (int i = 0; i < 100; i++)
    {
        stopwatch.Restart();
        var response = await SendRequest(httpClient, apiUrl);
        stopwatch.Stop();
        responseTimes.Add(stopwatch.ElapsedMilliseconds);
    }
 
    Console.WriteLine($"Reused connection response times: Min={responseTimes.Min()}ms Max={responseTimes.Max()}ms Avg={responseTimes.Average()}ms");
}
 
public static async Task<string> SendRequest(HttpClient httpClient, string url)
{
    var response = await httpClient.GetAsync(url);
    response.EnsureSuccessStatusCode();
    return await response.Content.ReadAsStringAsync();
}

When running the example above, the first request opens a new connection, and the subsequent requests reuse the existing connection. This is verified by observing the reduced time for reuse requests.

For example, the results might look like this:

  • Cost of opening a new connection: 524ms
  • Cost of reusing the connection: Min=50ms Max=60ms Avg=55ms

In this case, the request that had to open a new connection took 524ms, while the reused connections took 55ms on average, making reuse around 9.5 times faster than opening a new connection.

Reusing connections is highly effective, providing performance boosts between 5x to 9.5x in most scenarios.