【问题标题】:Many connections in TIME_WAIT state in connection pool, using either static or single HttpClient instance in WebApi连接池中许多处于 TIME_WAIT 状态的连接,在 WebApi 中使用静态或单个 HttpClient 实例
【发布时间】:2020-09-11 12:15:08
【问题描述】:

我一直在努力尝试在使用 HttpClient 时阻止池中的多个连接

我已经构建了一个 REST WebApi,我通过在其中使用 HttpClient 来调用外部端点。客户端多次调用控制器的动作以执行上述外部调用。

以前我使用的是 WebClient,但是当发出多个请求时,它遇到了 SocketExhaustation。在网上搜索我发现迁移到 HttpWebClient 将解决我的问题,如果使用静态或在单例中。我什至尝试实现 IHttpClientFactory 但它没有用,因为我在注入的服务中遇到了同样的问题。

这是我当前的实现,经过几次重构。抱歉,如果目前此代码不是最佳的,但经过多次重写,这是我最后的选择。

public static class CustomHttpClientManager
    {
        private static HttpClient _httpClient;

        public static void Initialize()
        {
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;

            ServicePointManager.ServerCertificateValidationCallback +=
                    (sender, cert, chain, sslPolicyErrors) => true;

            CookieContainer cookies = new CookieContainer();
            _httpClient = new HttpClient(
            new SocketsHttpHandler()
            {
                Proxy = new CustomProxy(),
                UseProxy = true,
                PooledConnectionLifetime = TimeSpan.FromSeconds(30),
                PooledConnectionIdleTimeout = TimeSpan.FromSeconds(20),
                MaxConnectionsPerServer = 2,
                CookieContainer = cookies,
                AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
            });

            _httpClient.DefaultRequestHeaders.ConnectionClose = false;
        }

        public static async Task<string> GetAsString(string url){
            HttpResponseMessage response = null;
            try{

                HttpRequestMessage httpRequestMessage = new HttpRequestMessage()
                {
                    RequestUri = new Uri(url),
                    Method = HttpMethod.Get
                };

                httpRequestMessage.Headers.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
                httpRequestMessage.Headers.Add("Accept-Encoding", "gzip,deflate,sdch");
                httpRequestMessage.Headers.Add("Accept-Language", "en-GB,en-US;q=0.8,en;q=0.6");
                httpRequestMessage.Headers.Add("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.3");
                
                response = await _httpClient.SendAsync(httpRequestMessage);    
                if(response.IsSuccessStatusCode){
                    return await response.Content.ReadAsStringAsync();   
                }
                else{
                    throw new Exception(ex.Message);
                }
            }
            catch(Exception ex){
                return $"Could not fetch response from: {connectionParameters.Url}, due to: {ex.Message}";     
            }
            finally{
                if(response != null){
                    response.Dispose();
                }
            }
        }
    }

直到到达当前代码的调用栈是这样的:

  1. 在启动时,WebApi 应用程序调用 CustomHttpClientManagerInitialize 方法,该方法仅将 HttpClient 实例化一次。
  2. 客户端调用 WebApi 控制器的操作。
  3. 动作调用业务逻辑类(服务类)
  4. 逻辑类使用CustomHttpClientManagerGetAsString方法通过使用先前实例化的HttpClient执行外部调用

客户端对 WebApi 执行大量调用(大约每分钟 200 次 rq),在命令提示符下运行 netstat -ano 时,它显示了一个相当长的池连接列表,其状态为 TIME_WAIT,这表明 @ 987654330@ 根本没有重用池中的连接。这些连接仅在使用后 2 分钟左右消失。

除了这段代码,我的其他方法是。

  • 在启动时使用.AddHttpClient() 注入IHttpClientFactory
  • 使用上述相同的类,但使用线程安全的单例版本。

他们都没有工作。我不知道还能做什么。这让我疯狂了大约 2 天。

我已经从一个简单的控制台应用程序测试了这个实现,但问题没有发生。这表明,一旦对调用者作出答复,WebApi 操作就会以某种方式不断关闭外部连接,并且一旦进行了另一个调用,它就会在池中创建另一个连接,并且它也会继续为下一个传入请求。

顺便说一句,我使用的是 .NET Core 3.1

提前致谢

【问题讨论】:

  • 使用IHttpClientFactory 是可行的方法,应该可以解决问题。当您尝试时,不知道出了什么问题。但这肯定是我最常推荐的方式。
  • @Fildor 我还发现IHttpClientFactory 是最推荐的方法,但是,我尝试注入到我正在使用的服务类中,当它不起作用时我尝试通过操作的控制器注入它(如果在使用 .AddHttpClient() 时未指定服务类,则可以这样做)我正在调用,但这种方法也具有相同的行为。
  • 很难说,没有看到这种方法。也许你只是错过了一英寸。
  • @Fildor 我怀疑我是否错过了一些东西,因为我只是按照docs.microsoft.com/en-us/dotnet/architecture/microservices/… 的 MSDocs 示例并以最简单的方式实现它。我刚刚使用了 'services.AddHttpClient();' 然后在 MyService 我只是通过其构造函数设置 HttpClient 字段,如"Implement your Typed Client classes that use the injected and configured HttpClient" 部分所述,仅此而已。
  • 您确实设置了 Handler Lifttime? services.AddHttpClient&lt;IMyService, MyService&gt;() .SetHandlerLifetime(TimeSpan.FromMinutes(5));

标签: c# asp.net-core .net-core httpclient connection-pooling


【解决方案1】:

使用

PooledConnectionLifetime = TimeSpan.FromSeconds(30),

表示连接在 30 秒后从池中移除。

您可以将其更改为更长(例如 10 分钟) - 机器人不会太长以适应 DNS 更改。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-15
    • 1970-01-01
    • 1970-01-01
    • 2017-04-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多