最近我在StackOverflow上问了一个关于asp.net核心中长期运行的后台任务的问题。从那以后,我尝试了这里的一切https://learn.microsoft.com/cs-cz/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-5.0&tabs=视觉工作室,如果我做我所做的,它会在某个时候停止。即使我把它包装在IServiceProvider.CreateScope中并等待它,它也会停止。我唯一没有尝试的事情,我正在努力避免它,就是创建一个专用的.net应用程序,它只会读取队列并做它应该做的事情。我还认为为它创建队列太过分了,我只想在后台异步运行它,但它只是停止了。如果这是一个愚蠢的错误,很抱歉,但这是我的第一个asp.net项目,我正在修复这个问题一周了。
这是队列版本
public class QueuedHostedService : BackgroundService
{
private readonly IServiceProvider _serviceProvider;
private readonly IBackgroundTaskQueue _queue;
private readonly ILogger<QueuedHostedService> _logger;
public QueuedHostedService(IServiceProvider serviceProvider, IBackgroundTaskQueue queue, ILogger<QueuedHostedService> logger)
{
_serviceProvider = serviceProvider;
_queue = queue;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await BackgroundProcessing(stoppingToken);
}
private async Task BackgroundProcessing(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var workItem = await _queue.DequeueAsync(stoppingToken);
try
{
var scope = _serviceProvider.CreateScope();
var scrapeUrl = scope.ServiceProvider.GetRequiredService<IScopedScrapeUrl>();
// The scrape Sound Cloud Task is taking hours
await scrapeUrl.ScrapeSoundCloud(workItem);
}catch(Exception ex)
{
_logger.LogError($"Error occurred executing {nameof(workItem)},n{ex}");
}
}
}
public override async Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Consume Scoped Scrape Url Hosted Service is stopping.");
await base.StopAsync(stoppingToken);
}
public class BackgroundScrapeQueue : IBackgroundTaskQueue
{
private readonly Channel<Scrape> _queue;
private readonly ILogger<BackgroundScrapeQueue> _logger;
public BackgroundScrapeQueue(ILogger<BackgroundScrapeQueue> logger)
{
var options = new BoundedChannelOptions(100)
{
FullMode = BoundedChannelFullMode.Wait
};
_queue = Channel.CreateBounded<Scrape>(options);
_logger = logger;
}
public async ValueTask<Scrape> DequeueAsync(CancellationToken stoppingToken)
{
var workItem = await _queue.Reader.ReadAsync(stoppingToken);
return workItem;
}
public async ValueTask QueueBackgroundWorkItemAsync(Scrape scrape)
{
if(scrape == null)
{
_logger.LogError("Invalid Scrape for queue");
return;
}
await _queue.Writer.WriteAsync(scrape);
}
}
public interface IBackgroundTaskQueue
{
ValueTask QueueBackgroundWorkItemAsync(Scrape scrape);
ValueTask<Scrape> DequeueAsync(CancellationToken stoppingToken);
}
它会在某个时候停止。即使我把它包装在IServiceProvider.CreateScope中并等待它,它也会停止。
是。这就是内存中后台服务的问题。它们可以随时停止,因为它们托管在ASP.NET进程中,该进程确定在请求完成时关闭是安全的。ASP.NET实际上会请求关闭,然后等待一段时间等待服务完成,但也有一个计时器,如果在10分钟左右内没有完成,它们将被迫退出。
底线是停机是正常的。任何假设它可以在ASP.NET中无限期运行的代码都是有缺陷的。
我唯一没有尝试的事情,我正在努力避免它,就是创建一个专用的.net应用程序,它只会读取队列并做它应该做的事情。
这是唯一可靠的解决方案。