我在一个空的ASP.NET Core8最小API项目中有一个基本的背景类.
应用程序启动只是:
builder.Services.AddHttpClient();
builder.Services.AddHostedService<SteamAppListDumpService>();
后台类用于保存STeam API端点的快照,所有基本内容如下:
public class SteamAppListDumpService : BackgroundService
{
static TimeSpan RepeatDelay = TimeSpan.FromMinutes(30);
private readonly IHttpClientFactory _httpClientFactory;
private string GetSteamKey() => "...";
private string GetAppListUrl(int? lastAppId = null)
{
return $"https://api.steampowered.com/IStoreService/GetAppList/v1/?key={GetSteamKey()}" +
(lastAppId.HasValue ? $"&last_appid={lastAppId}" : "");
}
public SteamAppListDumpService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await DumpAppList();
await Task.Delay(RepeatDelay, stoppingToken);
}
}
public record SteamApiGetAppListApp(int appid, string name, int last_modified, int price_change_number);
public record SteamApiGetAppListResponse(List<SteamApiGetAppListApp> apps, bool have_more_results, int last_appid);
public record SteamApiGetAppListOuterResponse(SteamApiGetAppListResponse response);
protected async Task DumpAppList()
{
try
{
var httpClient = _httpClientFactory.CreateClient();
var appList = new List<SteamApiGetAppListApp>();
int? lastAppId = null;
do
{
using var response = await httpClient.GetAsync(GetAppListUrl(lastAppId));
if (!response.IsSuccessStatusCode) throw new Exception($"API Returned Invalid Status Code: {response.StatusCode}");
var responseString = await response.Content.ReadAsStringAsync();
var responseObject = JsonSerializer.Deserialize<SteamApiGetAppListOuterResponse>(responseString)!.response;
appList.AddRange(responseObject.apps);
lastAppId = responseObject.have_more_results ? responseObject.last_appid : null;
} while (lastAppId != null);
var contentBytes = JsonSerializer.SerializeToUtf8Bytes(appList);
using var output = File.OpenWrite(Path.Combine(Config.DumpDataPath, DateTime.UtcNow.ToString("yyyy-MM-dd__HH-mm-ss") + ".json.gz"));
using var gz = new GZipStream(output, CompressionMode.Compress);
gz.Write(contentBytes, 0, contentBytes.Length);
}
catch (Exception ex)
{
Trace.TraceError("skipped...");
}
}
}
API总共返回大约16 MB的数据,然后每隔30分钟将其压缩/保存为4 MB的文件.在运行期间,当垃圾收集器运行时,我预计内存消耗将下降到几乎为零,但随着时间的推移,它会增加,例如,它已经在我的PC上运行了2个小时,消耗了700MB内存.在我的服务器上,它已经运行了24小时,现在占用了2.5 GB的内存.
据我所知,所有的流都被释放了,HttpClient
是使用推荐的IHttpClientFactory
创建的,有人知道为什么这个基本功能在垃圾收集之后还要消耗这么多内存吗?我试着在VS管理内存转储中查看它,但找不到太多有用的东西.这是指向某个类(即HttpClient
/SerializeToUtf8Bytes
)中的内存泄漏,还是我遗漏了什么?
responseString
和contentBytes
的大小通常在2MB左右.