在使用 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应用程序时,调用CustomHttpClientManager的Initialize方法,实例化HttpClient。
2.客户端调用WebApi控制器的操作。
3.该操作调用业务逻辑类(服务类)。
4.逻辑类使用CustomHttpClientManager的GetAsString方法执行外部调用,使用先前实例化的HttpClient。
客户端对WebApi进行了大量调用(每分钟约200个请求),在命令提示符上运行netstat -ano时,它显示了一个相当长的池连接列表,其状态为TIME_WAIT,这表明HttpClient根本不重用来自池的连接。这些连接只有在使用后约两分钟才会消失。
除了这段代码之外,我的其他方法是:
1.在Startup中注入IHttpClientFactory,并使用.AddHttpClient()。
2.使用上述相同的类,但使用线程安全的单例版本。
以上两种方法都没有起作用。我不知道还能做什么。这让我疯狂了将近两天。
我已经从简单的控制台应用程序中测试过此实现,问题并未发生。这告诉我,某种方式WebApi操作保持关闭外部连接一旦给出其调用者的回复,一旦进行另一次调用,它就会在池中创建另一个连接,并且对于下一个传入的请求也会一直如此。
顺便说一下,我正在使用.NET Core 3.1。
提前致谢。
IHttpClientFactory
是正确的方法,应该可以解决问题。不知道你尝试时出了什么问题。但这绝对是我发现最常推荐的方法。 - Fildorservices.AddHttpClient<IMyService, MyService>() .SetHandlerLifetime(TimeSpan.FromMinutes(5));
- Fildorservices.AddHttpClient
注册了瞬态服务。所以每个请求=>新服务=>新的HttpClient...但是它们共享一个处理程序。您只需要设置其生命周期即可。 - Fildor