Home > Back-end >  ASP.Net Core 3.1 HttpClient does not reuse sockets
ASP.Net Core 3.1 HttpClient does not reuse sockets

Time:04-13

I have the below piece of code which I ran on Asp.net 4.7.2 and .Net Core but I have got different behavior for each framework

public class Program
{


    private HttpClient Client = new HttpClient();
    public static async Task Main(string[] args)
    {
        Program example = new Program();

        Console.WriteLine("Starting connections");
        int numberofIterations = 10;
        Task<HttpResponseMessage>[] awaitableTasks = new Task<HttpResponseMessage>[numberofIterations];
        for (int i = 0; i < numberofIterations; i  )
        {
            var httpRequestMessage = new HttpRequestMessage();
            httpRequestMessage.RequestUri = new Uri("https://example.com");
            httpRequestMessage.Method = new HttpMethod("GET");

            awaitableTasks[i] = example.Client.SendAsync(httpRequestMessage);
            //Console.WriteLine(result.StatusCode);
        }

        Console.WriteLine("Connections done");
        await Task.WhenAll(awaitableTasks);

    }
}

With the .Net Core framework, the network traces shows a separate tcp connection for each request while with the ASP.NEt 4.7.2 framework the sockets get reused.

Network Trace .Net Core

enter image description here

Network Trace Asp.Net 4.7.2

enter image description here

Appreciate your thoughts to understand the differences, to explain this behavior and the best way to overcome this issue.

CodePudding user response:

Have you seen this resource:

Using HttpClientFactory without dependency injection

If you are on .NET Core - you should use a single HttpClient directly and set SocketsHttpHandler.PooledConnectionTimeout here to an appropriate value.

If you are on .NET Framework - you should use a single HttpClient and use ServicePoint to configure the similar settings.

The good news for anyone interested in connection management is that .NET now has reasonable behavior on Linux (as of 2.1 and SocketsHttpHandler) but it requires configuration.

More detailed information found in https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-3.1#alternatives-to-ihttpclientfactory-2

There are alternative ways to solve the preceding problems using a long-lived SocketsHttpHandler instance.

Create an instance of SocketsHttpHandler when the app starts and use it for the life of the app. Configure PooledConnectionLifetime to an appropriate value based on DNS refresh times. Create HttpClient instances using new HttpClient(handler, disposeHandler: false) as needed. The preceding approaches solve the resource management problems that IHttpClientFactory solves in a similar way.

The SocketsHttpHandler shares connections across HttpClient instances. This sharing prevents socket exhaustion. The SocketsHttpHandler cycles connections according to PooledConnectionLifetime to avoid stale DNS problems.

Also possibly relevant (but more focused on DI):

Use IHttpClientFactory to implement resilient HTTP requests

Here are a few relevant excerpts:

The original and well-known HttpClient class can be easily used, but in some cases, it isn't being properly used by many developers.

Though this class implements IDisposable, declaring and instantiating it within a using statement is not preferred because when the HttpClient object gets disposed of, the underlying socket is not immediately released, which can lead to a socket exhaustion problem. For more information about this issue, see the blog post You're using HttpClient wrong and it's destabilizing your software.

....

HttpClient lifetimes

Each time you get an HttpClient object from the IHttpClientFactory, a new instance is returned. But each HttpClient uses an HttpMessageHandler that's pooled and reused by the IHttpClientFactory to reduce resource consumption, as long as the HttpMessageHandler's lifetime hasn't expired.

CodePudding user response:

Short Answer, the below modification to the code will fix the .net core pooling issue

 var socketsHandler = new SocketsHttpHandler
            {
                PooledConnectionLifetime = TimeSpan.FromSeconds(60),
                PooledConnectionIdleTimeout = TimeSpan.FromMinutes(20),
                MaxConnectionsPerServer = 2
            };

            HttpClientHandler handler = new HttpClientHandler() { MaxConnectionsPerServer = 2 };

            var Client = new HttpClient(handler);

Long answer to understand the difference between the .net framework 4.x.x and .Net core and the connection pooling in ,net core read the below article. https://www.stevejgordon.co.uk/httpclient-connection-pooling-in-dotnet-core

Also the above answer from @user700390 is a great one.

  • Related