连接池中许多处于TIME_WAIT状态的连接,使用WebApi中的静态或单个HttpClient实例



我已经挣扎了好几天,试图在使用HttpClient 时防止池中的多个连接

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

以前我使用的是WebClient,但在发出几个请求时,它遇到了SocketExhausting问题。在网上搜索时,我发现如果静态使用或在单例中使用,迁移到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,这表明HttpClient根本没有重用池中的连接。这些连接仅在使用后2分钟左右消失。

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

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

它们都不起作用。我不知道还能做什么。这已经让我连续两天发疯了。

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

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

提前感谢

使用

PooledConnectionLifetime=TimeSpan.FromSeconds(30(,

意味着连接在30秒后从池中删除。

你可以把它改得更长(例如10分钟(-机器人不会太长而无法适应DNS改变。

相关内容

最新更新