4

I have a .Net Core 3.1 app. In the app, I use the IHttpClientFactory to create an HttpClient. When I make a call using SendAsync, the first request takes over 2 seconds whereas subsequent requests take less than 100 ms. This is not acceptable performance for a production application.

I have also noticed that it happens if I don't make any requests for a while. I came across the PooledConnectionIdleTimeout property, which defaults to 2 minutes, and I can extend that time, but that would only work for pooled connections that already exist, not when needing to create a new one.

I configure the HttpClient in my Startup.cs as such:

services.AddHttpClient("Default")
                    .ConfigurePrimaryHttpMessageHandler(() =>
                    {
                        return new SocketsHttpHandler
                        {
                            UseCookies = false,
                            AllowAutoRedirect = false,
                            MaxConnectionsPerServer = int.MaxValue,
                            UseProxy = false
                        };
                    });

I have a Singleton wrapper class which contains a method which calls SendAsync:

public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken = default)
        {
            var client = m_HttpClientFactory.CreateClient("Default");
            return await client.SendAsync(request, completionOption, cancellationToken).ConfigureAwait(false);
        }

Is there some kind of configuration that I'm missing? Or some kind of warm up that I can do so that the first request doesn't take a long time?

UPDATE: It's been a few months and I don't remember all of the details, but considering a recent comment, I decided to post my findings and my workaround.

I found that this issue did not occur when using an IP address for the hostname rather than the server name. In my case, I'd almost always be calling localhost, so I do a comparison the hostname I'm supposed to reach out to and if it matches the local system's host name, I change the host to 127.0.0.1.

IIRC, the .Net Core logic will try to fetch all IPs for the server name and then go through each one, one by one, and try to connect successfully. Depending on how many it has to cycle through will increase the time by 1 second per failed attempt. When I had IPv6 enabled on my system, that doubled the amount of IPs it would potentially try.

Hope that helps and sorry if it sounds vague. It's been a while :)

Steven C
  • 41
  • 3
  • welcome. theres lots of things going on in there - code compilation (JIT), dns query, tcp connection, tls negotiation, etc... – Daniel A. White Jun 12 '20 at 20:35
  • Did u resolve that problem? I am running to a similiar case – TheTanic Dec 03 '20 at 10:52
  • @TheTanic I posted an update in the question with how I resolved the issue for myself. Hope it helps or at least leads you in the right direction. – Steven C Dec 04 '20 at 15:10
  • It's the OS that performs DNS resolution, not .NET Core. If a DNS name resolves to multiple IPs, the OS has to try them all until a good one is found, doesn't it? That's why HttpClient caches connections (specifically, socket connections) and why it should be reused. DNS resolution can take up to 15 seconds – Panagiotis Kanavos Dec 04 '20 at 15:14
  • Unfortunatly that didn't helped in my case. I can live with this starting behaviour, because i will send data in short time intervals and it reuses the connection, but its still not really optimal – TheTanic Dec 07 '20 at 07:23

1 Answers1

0

An other working solution using SocketsHttpHandler and HttpClient here : https://stackoverflow.com/a/70475741/1529139

It uses IPv4 over IPv6 at connection time (that's why it is slow sometimes, like ~2s)

It did the job for me

56ka
  • 1,463
  • 1
  • 21
  • 37