我有一个 C# .Net 8 Minimal API 应用程序,其中有一项服务使用 HTTPClient 与不同的 API 进行通信。关键是对我的应用程序的一个请求会触发对不同 API 的数千个请求,并且与不同 API 的通信可能需要几个小时。
设置如下:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IService, MyService>();
builder.Services.AddHttpClient<IApi, Api>();
var app = builder.Build();
app.UseRouting();
app.MapPost("/start", Func)
app.Run();
static async Task<IResult> Func(List<Object> os, IService service, IApi api)
{
service.SetApi(api);
// What happens if the await is removed? Request is done and service and api will be cleaned up, or?
await service.StartLongJobWithDifferentAPI(os);
return Results.Ok();
}
public async Task<List<string>> StartLongJobWithDifferentAPI(List<Object> os)
{
var tasks = new List<Task<string>>();
foreach (var o in os)
{
tasks.Add(((Func<Task<string>>)(async (o) =>{
var response = await _apiclient.PostAsJsonAsync("/post", o);
return await response.Content.ReadAsStringAsync();})));
}
return (await Task.WhenAll(tasks)).ToList();
}
所以我的问题是: 如果删除等待,请求将以 Results.Ok() 结束,服务和 HTTPClient api 被清理,正在运行的任务将被中止,或者?
但是任务仍在运行,因此垃圾收集器不会清理任何东西,或者?
对我来说,不等待似乎是非常糟糕的做法,因为你失去了对错误管理(异常,404,...)的控制,并且你可能最终会在不受控制的情况下运行大量僵尸任务。 或者有人对长时间运行的任务使用这种不等待模式吗?
另一方面,“/start”端点的职责只是启动进程,我们对响应不感兴趣。
首先 - 避免使用“即发即弃”任务,最好至少使用类似 ASP.NET Core 中托管服务的后台任务,这将允许您管理并发并监控任务的完成情况。
如果去掉await会发生什么?请求完成,服务和 api 将被清理,或者?
这取决于细节。据我所知,默认情况下,注入的
HttpClient
不会在作用域(在 ASP.NET Core 处理程序/操作的情况下为 == 请求)时被处理,但是如果您在 IDisposable
中实现 IApi
那么就会的。例如以下内容:
public class Api(HttpClient client) : IApi, IDisposable
{
public HttpClient HttpClient => client;
public void Dispose()
{
client.Dispose();
}
}
可能会导致在正确完成处理请求之前处置
HttpClient
:
var serviceCollection = new ServiceCollection();
serviceCollection.AddHttpClient<IApi, Api>();
var serviceProvider = serviceCollection.BuildServiceProvider();
IApi? service;
using (var sp = serviceProvider.CreateScope())
{
service = sp.ServiceProvider.GetService<IApi>();
}
var serviceHttpClient = service.HttpClient;
// prints "True" for the previous implementation
Console.WriteLine(typeof(HttpClient).GetField("_disposed", BindingFlags.Instance |BindingFlags.NonPublic).GetValue(serviceHttpClient));