几个小时后并行发送 HTTP 请求后,ServicePoint 的大小 object
Big size of ServicePoint object after several hours sending HTTP request in parallel
我们正在使用 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
objects,其中一个很大,大约 160 MB。
据我所知,我发现上面的代码存在一些问题:
- 我们应该尽可能共享
HttpClient
个实例。在我们的例子中,请求地址和 headers 可能会有很大差异,所以这是我们可以做某事的一点还是不会对性能造成太大影响?我只是想到我们可以准备一个字典来存储和查找HttpClient
个实例。
- 我们没有修改
ServicePoint
的DefaultConnectionLimit
,所以默认只能同时向同一个服务器发送两个请求。如果我们把这个值改大一点,内存泄漏的问题就可以解决了吗?
- 我们还抑制了 HTTPS 证书验证:
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
这与问题有关吗?
由于这个问题不容易重现(需要很多时间),我只是需要一些想法,以便我可以长期优化我们的代码运行。
自己说明一下,以防以后其他人也遇到这个问题。
首先,这不是内存泄漏,而是性能问题。
We test our application in virtual machine, on which we opened the proxy. It leads to the internet is quite slow. So in our case, each HTTP request might cost 3-4 seconds. As time going, there will be a lot of connections in the ServicePoint
queue. Therefore, it's not memory leaks, that's just because the previous connections are not finished quickly enough.
只要确保每个 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;
}
在我们的例子中,对于相同的服务器地址,只有请求正文不同。我还覆盖了 HttpRequestInfo
的 Equals
和 GetHashCode
方法。
同时,我们设置ServicePointManager.DefaultConnectionLimit = int.MaxValue;
希望对您有所帮助。
我们正在使用 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
objects,其中一个很大,大约 160 MB。
据我所知,我发现上面的代码存在一些问题:
- 我们应该尽可能共享
HttpClient
个实例。在我们的例子中,请求地址和 headers 可能会有很大差异,所以这是我们可以做某事的一点还是不会对性能造成太大影响?我只是想到我们可以准备一个字典来存储和查找HttpClient
个实例。 - 我们没有修改
ServicePoint
的DefaultConnectionLimit
,所以默认只能同时向同一个服务器发送两个请求。如果我们把这个值改大一点,内存泄漏的问题就可以解决了吗? - 我们还抑制了 HTTPS 证书验证:
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
这与问题有关吗?
由于这个问题不容易重现(需要很多时间),我只是需要一些想法,以便我可以长期优化我们的代码运行。
自己说明一下,以防以后其他人也遇到这个问题。
首先,这不是内存泄漏,而是性能问题。
We test our application in virtual machine, on which we opened the proxy. It leads to the internet is quite slow. So in our case, each HTTP request might cost 3-4 seconds. As time going, there will be a lot of connections in the
ServicePoint
queue. Therefore, it's not memory leaks, that's just because the previous connections are not finished quickly enough.
只要确保每个 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;
}
在我们的例子中,对于相同的服务器地址,只有请求正文不同。我还覆盖了 HttpRequestInfo
的 Equals
和 GetHashCode
方法。
同时,我们设置ServicePointManager.DefaultConnectionLimit = int.MaxValue;
希望对您有所帮助。