本地配置:
.NET 8.0的WebApi项目,许多基础配置不在这里重复描述。
最开始考虑到响应时间采用的是多线程的方案,但是为了程序健壮性后面就改用任务队列了。
一、创建通用后台任务队列接口
首先,我们创建一个接口来定义后台任务队列的行为。
cs
using Microsoft.Extensions.DependencyInjection;
namespace CS_FeedbackApi.IService
{
public interface IBackgroundTaskQueue
{
void EnqueueTask(Func<IServiceProvider, Task> task);
Func<IServiceProvider, Task> DequeueTask();
}
}
二、实现通用后台任务队列
接下来,我们实现这个接口。这个队列将存储要执行的异步任务委托。
cs
using CS_FeedbackApi.IService;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Concurrent;
namespace CS_FeedbackApi.Service
{
public class BackgroundTaskQueue : IBackgroundTaskQueue
{
private readonly ConcurrentQueue<Func<IServiceProvider, Task>> _tasks = new();
public void EnqueueTask(Func<IServiceProvider, Task> task)
{
_tasks.Enqueue(task);
}
public Func<IServiceProvider, Task> DequeueTask()
{
_tasks.TryDequeue(out var task);
return task;
}
}
}
三、创建新的后台服务来处理队列
这个新的后台服务会为每个任务创建一个新的依赖注入作用域,从而可以解决"服务被俘获"的问题。
cs
using CS_FeedbackApi.IService;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace CS_FeedbackApi.Service
{
public class QueuedHostedService : BackgroundService
{
private readonly ILogger<QueuedHostedService> _logger;
private readonly IBackgroundTaskQueue _taskQueue;
private readonly IServiceProvider _serviceProvider;
public QueuedHostedService(IBackgroundTaskQueue taskQueue, ILogger<QueuedHostedService> logger, IServiceProvider serviceProvider)
{
_taskQueue = taskQueue;
_logger = logger;
_serviceProvider = serviceProvider;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Queued Hosted Service is running.");
while (!stoppingToken.IsCancellationRequested)
{
var workItem = _taskQueue.DequeueTask();
if (workItem != null)
{
try
{
_logger.LogInformation("Executing background task.");
// 为每个任务创建新的作用域来解析范围服务
using (var scope = _serviceProvider.CreateScope())
{
await workItem(scope.ServiceProvider);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occurred executing background task.");
}
}
else
{
await Task.Delay(500, stoppingToken);
}
}
_logger.LogInformation("Queued Hosted Service is stopping.");
}
}
}
四、更新 Program.cs 以注册新服务
需要在 Program.cs 中注册新的服务
cs
//后台服务
builder.Services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();
builder.Services.AddHostedService<QueuedHostedService>();
五、接口中的使用
在需要使用的代码块中,将具体方法进行如下替换即可
cs
_taskQueue.EnqueueTask(async sp =>
{
var authService = sp.GetRequiredService<IAuthService>();
//这里替换为具体你所写好的方法
await authService.SendTemplateTiJiaoAsync(model, result);
});