我们正在使用HttpClient
并行发送请求到远程 Web API:
public async Task<HttpResponseMessage> PostAsync(HttpRequestInfo httpRequestInfo)
{
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(httpRequestInfo.BaseUrl);
if (httpRequestInfo.RequestHeaders.Any())
{
foreach (var requestHeader in httpRequestInfo.RequestHeaders)
{
httpClient.DefaultRequestHeaders.Add(requestHeader.Key, requestHeader.Value);
}
}
return await httpClient.PostAsync(httpRequestInfo.RequestUrl, httpRequestInfo.RequestBody);
}
}
该API可以由多个线程同时调用。运行大约四个小时后,我们发现发生了内存泄漏问题,从分析工具来看,似乎有两个问题ServicePoint
对象,其中一个相当大,大约 160 MB。
据我所知,我可以看到上面的代码存在一些问题:
- 我们应该分享
HttpClient
尽可能地举例。在我们的例子中,请求地址和标头可能会有很大差异,所以这是我们可以做的事情还是不会损害太多性能?我只是想到我们可以准备一个字典来存储和查找HttpClient
实例。
- 我们没有修改
DefaultConnectionLimit
of ServicePoint
,所以默认情况下它只能同时向同一台服务器发送两个请求。如果我们把这个值改大一点,内存泄漏问题就可以解决了吗?
- 我们还取消了 HTTPS 证书验证:
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
这和问题有关系吗?
由于这个问题不容易重现(需要很多时间),我只需要一些想法,以便我可以优化我们的代码以长时间运行。
我自己解释一下情况,以防其他人以后也遇到这个问题。
首先,这不是内存泄漏,而是性能问题。
我们在虚拟机中测试我们的应用程序,并在虚拟机上打开代理。这导致互联网速度相当慢。因此,在我们的例子中,每个 HTTP 请求可能花费 3-4 秒。随着时间的推移,将会有很多联系ServicePoint
队列。因此,这不是内存泄漏,而是因为之前的连接没有足够快地完成。
只要确保每个 HTTP 请求不是那么慢,一切就会变得正常。
我们也尝试减少HttpClient
实例,以提高 HTTP 请求性能:
private readonly ConcurrentDictionary<HttpRequestInfo, HttpClient> _httpClients;
private HttpClient GetHttpClient(HttpRequestInfo httpRequestInfo)
{
if (_httpClients.ContainsKey(httpRequestInfo))
{
HttpClient value;
if (!_httpClients.TryGetValue(httpRequestInfo, out value))
{
throw new InvalidOperationException("It seems there is no related http client in the dictionary.");
}
return value;
}
var httpClient = new HttpClient { BaseAddress = new Uri(httpRequestInfo.BaseUrl) };
if (httpRequestInfo.RequestHeaders.Any())
{
foreach (var requestHeader in httpRequestInfo.RequestHeaders)
{
httpClient.DefaultRequestHeaders.Add(requestHeader.Key, requestHeader.Value);
}
}
httpClient.DefaultRequestHeaders.ExpectContinue = false;
httpClient.DefaultRequestHeaders.ConnectionClose = true;
httpClient.Timeout = TimeSpan.FromMinutes(2);
if (!_httpClients.TryAdd(httpRequestInfo, httpClient))
{
throw new InvalidOperationException("Adding new http client thrown an exception.");
}
return httpClient;
}
在我们的例子中,对于相同的服务器地址,只有请求正文不同。我也覆盖Equals
and GetHashCode
的方法HttpRequestInfo
.
同时,我们设定ServicePointManager.DefaultConnectionLimit = int.MaxValue;
希望这可以帮助你。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)