HttpClient内存使用量激增,响应速度很快

时间:2017-06-01 14:27:36

标签: c# asp.net .net memory memory-leaks

我正在开发一个控制台应用程序,它将端点列表添加到视频数据中,发出HTTP请求,并将结果保存到文件中。这些是相对较小的视频。由于我无法控制的问题,其中一个视频非常大(145分钟而不是几秒钟)。

我看到的问题是,在调用该请求后,我的内存使用率达到~1 GB,并且我最终得到“任务被取消”错误(可能是因为客户端超时)。这很好,我不想要这个视频,但令人担忧的是,无论我做什么,我分配的内存都会保持很高。我希望能够释放内存。似乎有点关于任务管理器在此调用之前显示大约14 MB的内存使用量,然后继续逐渐涓流。在VS调试器中,我只看到一个尖峰。

我尝试将所有内容都放在using语句中,重新初始化HttpClient异常,手动调用GC.Collect()而没有运气。我正在使用的代码看起来像这样:

consumer.Received += async (model, ea) =>
{
    InitializeHttpClient(source);
    ...
    foreach(var item in queue)
    {
        await SaveFileFromEndpoint(url, fileName);
        ...
    }
}

和方法:

public void InitializeHttpClient(string source)
{
    ...
    _client = new HttpClient();
    ...
}

public async Task SaveFileFromEndpoint(string endpoint, string fileName)
{
    try
    {
        using (HttpResponseMessage response = await _client.GetAsync(endpoint))
        {
            if (response.IsSuccessStatusCode)
            {
                using(var content = await response.Content.ReadAsStreamAsync())
                using (var fileStream = File.Create($"{fileName}"))
                {
                    await response.Content.CopyToAsync(fileStream);
                }
            }
        }
    }
    catch (Exception ex)
    {

    }
}

以下是我的调试器输出:

memory spike

我想我对我所看到的内容有几个问题:

  1. 我看到的内存使用量实际上是个问题吗?
  2. 有没有办法可以释放大型HTTP请求分配的内存?
  3. 在调用和分配内存之前,有什么方法可以看到请求的内容长度吗?到目前为止,我还没有找到在分配实际内存之前查找的方法。
  4. 提前感谢您的帮助!

2 个答案:

答案 0 :(得分:5)

如果您使用HttpClient.SendAsync(HttpRequestMessage, HttpCompletionOption)代替GetAsync,则可以提供HttpCompletionOption.ResponseHeadersRead,(而不是默认ResponseContentRead)。这意味着响应流将在响应主体下载之前(而不是之后)回传给您,并且需要的缓冲区要少得多。

答案 1 :(得分:2)

除了@spender的答案(很明确)之外,您还需要确保在完成响应后将其处置。您可以在"Efficiently Streaming Large HTTP Responses With HttpClient"文章中找到有关此信息的更多信息。

这是一个代码示例:

using (HttpClient client = new HttpClient())
{
    const string url = "https://github.com/tugberkugurlu/ASPNETWebAPISamples/archive/master.zip";
    using (HttpResponseMessage response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead))
    using (Stream streamToReadFrom = await response.Content.ReadAsStreamAsync())
    {
        string fileToWriteTo = Path.GetTempFileName();
        using (Stream streamToWriteTo = File.Open(fileToWriteTo, FileMode.Create))
        {
            await streamToReadFrom.CopyToAsync(streamToWriteTo);
        }
    }
}

您还需要考虑到不应为每个操作创建一个HttpClient实例。 HttpClientFactory是一种井井有条的方式,可确保以最高效的方式安全地在应用程序中流动HttpClient。