在www.example.com core 3.x中按需运行后台任务asp.net

5n0oy7gb  于 2022-12-30  发布在  .NET
关注(0)|答案(3)|浏览(150)

每当我从我的API端点收到一个特定的请求时,我试图按需启动一个后台任务,所有的任务都是发送一封邮件,延迟30秒,所以我认为BackgroundService是合适的,但问题是它看起来像BackgroundService主要用于重复任务,而不是按需执行answer
那么我还有什么其他的选择呢?我希望不必依赖像Hangfire这样的第三方库?我正在使用asp.net core 3.1。
这是我的后台服务。

using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace ProjectX.Services {
    public  class EmailOfflineService : BackgroundService {

        private readonly ILogger<EmailOfflineService> log;
        private readonly EmailService emailService;
        public EmailOfflineService(
            ILogger<EmailOfflineService> log, 
            EmailService emailService
        ) {
            this.emailService = emailService;
            this.log = log;
        }

        protected async override Task ExecuteAsync(CancellationToken stoppingToken)
        {

            log.LogDebug("Email Offline Service Starting...");
            stoppingToken.Register(() => log.LogDebug("Email Offline Service is stopping."));

            while(!stoppingToken.IsCancellationRequested)
            {
                // wait for 30 seconds before sending
                await Task.Delay(1000 * 30, stoppingToken);

                await emailService.EmailOffline();
                
                // End the background service
                break;
            }
            log.LogDebug("Email Offline Service is stoped.");
        }
    }
}
h7appiyu

h7appiyu1#

您可以尝试将异步队列与BackgroundService结合使用。

public class BackgroundEmailService : BackgroundService
{
    private readonly IBackgroundTaskQueue _queue;

    public BackgroundEmailService(IBackgroundTaskQueue queue)
    {
        _queue = queue;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            var job = await _queue.DequeueAsync(stoppingToken);
            
            _ = ExecuteJobAsync(job, stoppingToken);
        }
    }

    private async Task ExecuteJobAsync(JobInfo job, CancellationToken stoppingToken)
    {
        try
        {
            await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
            // todo send email
        }
        catch (Exception ex)
        {
            // todo log exception
        }
    }
}

public interface IBackgroundTaskQueue
{
    void EnqueueJob(JobInfo job);

    Task<JobInfo> DequeueAsync(CancellationToken cancellationToken);
}

通过这种方式,您可以将IBackgroundTaskQueue注入到控制器中,并将作业排队到其中,而JobInfo将包含一些用于在后台执行作业的基本信息,例如:

public class JobInfo
{
    public string EmailAddress { get; set; }
    public string Body { get; set; }
}

后台队列示例(灵感来自ASP.NET核心文档):

public class BackgroundTaskQueue : IBackgroundTaskQueue
{
    private ConcurrentQueue<JobInfo> _jobs = new ConcurrentQueue<JobInfo>();
    private SemaphoreSlim _signal = new SemaphoreSlim(0);

    public void EnqueueJob(JobInfo job)
    {
        if (job == null)
        {
            throw new ArgumentNullException(nameof(job));
        }

        _jobs.Enqueue(job);
        _signal.Release();
    }

    public async Task<JobInfo> DequeueAsync(CancellationToken cancellationToken)
    {
        await _signal.WaitAsync(cancellationToken);
        _jobs.TryDequeue(out var job);

        return job;
    }
}
bqjvbblv

bqjvbblv2#

我认为最简单的方法是在处理发送电子邮件请求的代码中进行一个“发射后忘记”调用,如下所示-

//all done, time to send email
Task.Run(async () => 
{
    await emailService.EmailOffline(emailInfo).ConfigureAwait(false); //assume all necessary info to send email is saved in emailInfo
});

这将启动一个发送电子邮件的线程。代码将立即返回给调用者。在EmailOffline方法中,您可以根据需要包含时间延迟逻辑。请确保也包含错误日志记录逻辑,否则EmailOffline的异常可能会被默默地吞掉。
附言-回答Coastpear和FlyingV -
不需要考虑调用上下文的结束。这项工作将在一个单独的线程上完成,它完全独立于调用上下文。
我已经在生产中使用类似的机制几年了,到目前为止零问题。
如果你的站点不是特别忙碌,而且工作也不是很重要,这是最简单的解决方案。只要确保你在你的工作器中捕获并记录错误(在这个例子中是EmailOffline)。
如果您需要更可靠的解决方案,我建议使用成熟的队列产品,如AWS SQS,不要费心自己创建一个,创建一个真正好的队列系统并不容易。

0ve6wy6x

0ve6wy6x3#

使用Hangfire,它的后台方法功能非常棒,并免费为您提供一个漂亮的 Jmeter 板:https://docs.hangfire.io/en/latest/background-methods/index.html

相关问题