httpClient.GetAsync C# .NET 4.8

问题描述

我在使用 httpClient.GetAsync 时遇到内存泄漏问题。
在任务管理器中,进程保持高内存消耗,不会被释放。

这是我的代码

HttpClient httpClient = new HttpClient();
httpClient.Timeout = TimeSpan.FromMinutes(2);

try
{
    using (var response = await httpClient.GetAsync(fullURL))
    {
        if (!response.IsSuccessstatusCode)
        {
            Logger.Default.Error($"error code {(int)response.StatusCode} - {response.StatusCode}");
            return null;
        }
        using (MemoryStream memStream = new MemoryStream())
        {
            await response.Content.copyToAsync(memStream);
            Logger.Default.Debug($"finished reading response,sized {Math.Round(memStream.Length / Math.Pow(1024,2),2)} MB");
        }
    }
}
catch (TaskCanceledException ex)
{
    Logger.Default.Error($"Request timed out. {ex.Message}\n{ex.StackTrace}");
    return null;
}

但是,当我改用 httpClient.GetStreamAsync 并更改时:

await response.copyToAsync(memStream);

致:

await response.Content.copyToAsync(memStream);

内存确实在几秒钟后被释放。 但我更愿意使用 GetAsync,因为它为我提供了有关状态代码的信息,而 GetStreamAsync 没有。

我已经尝试在对象被释放后调用垃圾收集器 (GC.Collect(2)),但没有帮助。

我做错了什么?

解决方法

HTTPClient 之前给我带来了这样的问题。我发现这篇文章很有帮助:

https://www.aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

基本上,即使它实现了 IDisposable,您也可以在应用程序的整个生命周期内将它实例化为单例。这允许应用仅使用该 HTTPClient 连接,而不会导致连接未正确处理的问题。

如果您在 using 块中使用它,Windows 将在此状态下保持连接 240 秒。它由以下设置:

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay]

试试这个:

using System;
using System.Net.Http;

namespace ConsoleApplication
{
    public class Program
    {
        private static HttpClient Client = new HttpClient();
        public static async Task Main(string[] args) 
        {
            Console.WriteLine("Starting connections");
            for(int i = 0; i<10; i++)
            {
                var result = await Client.GetAsync("http://aspnetmonsters.com");
                Console.WriteLine(result.StatusCode);
            }
            Console.WriteLine("Connections done");
            Console.ReadLine();
        }
    }
}

如果通过 using 语句语法实现 IDisposable,连接实际上在 using 块结束后保持打开状态。然后,当您的应用程序遇到另一个 using 块 HTTPClient 时,先前存在的一次尚未关闭。这可能会导致各种错误,最常见的是:

Windows 打开新套接字的速度是有限制的,所以如果你耗尽了连接池,那么你很可能会看到如下错误:

Unable to connect to the remote server
System.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted.

互联网上的其他一些人找到了更好的方法来做到这一点,我个人没有使用它们的经验,但这里有一些值得一试的链接。这些描述了在不建立 HTTPClient 单例的情况下使用 IHTTPClientFactory 的方法和其他方法:

https://josef.codes/you-are-probably-still-using-httpclient-wrong-and-it-is-destabilizing-your-software/

https://www.stevejgordon.co.uk/httpclient-creation-and-disposal-internals-should-i-dispose-of-httpclient