【发布时间】:2015-03-17 10:18:29
【问题描述】:
我们正在使用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 可以被多个线程同时调用。运行大约四个小时后,我们发现发生了内存泄漏问题,从 profiling 工具中,似乎有两个 ServicePoint 对象,其中一个相当大,大约 160 MB。
据我所知,我可以看到代码上面的一些问题:
- 我们应该尽可能共享
HttpClient实例。在我们的例子中,请求地址和标头可能会有很大的不同,那么这是我们可以做些什么还是不会损害太多性能?我只是想到我们可以准备一个字典来存储和查找HttpClient实例。 - 我们没有修改
ServicePoint的DefaultConnectionLimit,所以默认只能同时向同一个服务器发送两个请求。如果我们把这个值改大一点,内存泄漏问题就可以解决了吗? - 我们还抑制了 HTTPS 证书验证:
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };这是否与问题有关?
由于这个问题不容易重现(需要很多时间),我只需要一些想法,以便我可以优化我们的代码以长时间运行。
【问题讨论】:
-
您使用的是哪种内存分析器,是否在检查哪些对象仍处于活动状态(msdn.microsoft.com/en-us/library/ee787088%28v=vs.110%29.aspx)之前强制对所有代进行堆生成清理?
-
@Alexandru 我使用 dotMemory,当然我强制 GC 收集发生并等待足够长的时间。无论如何,我已经找到了问题所在,这与互联网流量性能有关,而不是内存泄漏。
-
根据您的回答,下次发生这种情况时,您应该切断任何新连接,然后在一段时间后调用生成垃圾收集。这应该让旧的连接消失,你应该看看是否存在真正的内存泄漏。听起来您排队的请求多于您一次能够处理的数量,实际上,您的应用程序本身几乎被 DoS 攻击,因为它最终可能会耗尽内存。
-
您下次可以尝试做的事情(顺便说一句,这很有帮助)是要求客户从任务管理器中生成应用程序的转储,在 Visual Studio 中打开它以及您发送给客户的代码,并调试到您的应用程序中。查看应用程序的活动线程。您可能会看到很多线程等待发布。
标签: c# .net multithreading httpclient servicepoint