Yeh*_*lam 7 c# asp.net background-service asp.net-core
每当我从我的 api 端点收到某个请求时,我都会尝试按需启动后台任务。所有任务所做的就是发送一封电子邮件,延迟 30 秒。所以我虽然BackgroundService会适合。但问题是它看起来BackgroundService主要用于重复性任务,而不是根据此答案按需执行。
那么我还有什么其他选择,我希望不必依赖像 Hangfire 这样的 3rd 方库?我正在使用 asp.net 核心 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.");
}
}
}
Run Code Online (Sandbox Code Playgroud)
Fed*_*uma 12
您可以尝试将异步队列与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);
}
Run Code Online (Sandbox Code Playgroud)
通过这种方式,您可以IBackgroundTaskQueue在控制器内部注入并将作业排入队列,同时JobInfo将包含一些在后台执行作业的基本信息,例如:
public class JobInfo
{
public string EmailAddress { get; set; }
public string Body { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
后台队列示例(受ASP.NET Core 文档启发):
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;
}
}
Run Code Online (Sandbox Code Playgroud)
我认为最简单的方法是在处理发送电子邮件请求的代码中进行“即发即忘”调用,如下所示 -
//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
});
Run Code Online (Sandbox Code Playgroud)
这将启动一个线程来发送电子邮件。该代码将立即返回给调用者。在您的 EmailOffline 方法中,您可以根据需要包含时间延迟逻辑。确保其中也包含错误日志记录逻辑,否则来自 EmailOffline 的异常可能会被默默吞没。
PS - 对 Coastpear 和 FlyingV 的回答 -
无需关心调用上下文的结束。这项工作将在一个单独的线程上完成,该线程完全独立于调用上下文。
我在生产中使用类似的机制已经有几年了,到目前为止为零问题。
如果您的站点不是非常繁忙,并且工作并不重要,那么这是最简单的解决方案。只需确保您捕获并记录工作线程内部的错误(本例中为 EmailOffline)。
如果您需要更可靠的解决方案,我建议使用像AWS SQS这样成熟的队列产品,不必费心自己创建一个。创建一个真正好的队列系统并不是一件容易的事。
| 归档时间: |
|
| 查看次数: |
6252 次 |
| 最近记录: |