WebAPI-定时任务
WebAPI-定时任务
Hosted Services
IHostedService接口
IHostedService
是一个接口,它定义了两个方法:StartAsync(CancellationToken)
和 StopAsync(CancellationToken)
。这两个方法分别用于启动和停止后台服务。当你实现这个接口时,你需要自己处理所有的逻辑,包括如何开始、执行任务以及优雅地关闭服务
BackgroundService抽象类
BackgroundService
是一个抽象类,它实现了 IHostedService
接口,并且提供了一个默认的实现来简化开发过程。它引入了一个名为 ExecuteAsync(CancellationToken)
的虚方法,你可以在子类中重写这个方法来实现具体的业务逻辑。BackgroundService
会自动处理一些常见的任务,比如在应用程序关闭时正确地停止服务。
自动触发
BackgroundService
中定义的任务是自动触发的,无需手动触发。当你将一个实现了 IHostedService
接口(或继承自 BackgroundService
)的服务注册到 ASP.NET Core 的依赖注入容器中时,ASP.NET Core 会在应用程序启动时自动调用该服务的 StartAsync
方法,并在应用程序关闭时调用 StopAsync
方法
program
// 注册定时任务类
builder.Services.AddHostedService<MyTask>();
MyTask
public class MyTask: BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
// 执行你的任务逻辑
Console.WriteLine("执行定时任务: " + DateTime.Now);
// 等待一段时间再执行下一次
await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
}
}
/// <summary>
/// 如果存在需要释放的资源可以填写到这里
/// </summary>
public override void Dispose()
{
// 释放资源
base.Dispose();
}
}
Timer
在 .NET 中,System.Threading.Timer
类提供了一种简单的方式来执行定时任务。它允许你指定一个回调方法,在经过一段指定的时间后调用该方法。Timer
类非常适合用于需要定期或延时执行的任务
public Timer(
TimerCallback callback,
object state,
int dueTime,
int period
)
- callback:一个
TimerCallback
委托,指向当定时器触发时要调用的方法。 - state:传递给回调方法的任意对象。这个参数可以用来传递数据到回调方法中。
- dueTime:定时器启动前的初始延迟时间(以毫秒为单位)。
- period:定时器触发之间的间隔时间(以毫秒为单位)。如果设置为 -1 或
Timeout.Infinite
,则定时器仅触发一次。
public class MyTask : BackgroundService
{
private Timer _timer;
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
// 设置定时器,每30秒触发一次
_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(30));
// 返回一个永远不会完成的任务(已经完成的任务),以保持服务运行
// 替代了使用while循环来保持服务运行
return Task.CompletedTask;
}
private void DoWork(object state)
{
// 执行任务逻辑
Console.WriteLine("执行定时任务: " + DateTime.Now);
// 检查是否需要停止
if (stoppingToken.IsCancellationRequested)
{
_timer.Dispose();
}
}
public override void Dispose()
{
// 释放资源
_timer?.Dispose();
base.Dispose();
}
}
Cron表达式
cron表达式是一个字符串,通过crom表达式可以定义任务触发的时间
构成规则:分为6或者个域,由空格分开,每个域代表一个含义
秒 | 分钟 | 小时 | 日 | 月 | 周 | 年(可为空) |
---|
注:周和日应该只有一个出现,而为空的时候填写?
NCrontab
如果想要实现指定规则去执行定时任务的话,仅仅通过Hosted Services是不行的
通过导入第三方库NCrontab,解析cron表达式,通过Timer来配置循环执行的定时任务,再通过Hosted Services来配置好后台任务
注: 因为Dal层是scope注入范围,而定时任务是单例,直接注入会出现异常,使用IServiceScopeFactory获取scope的Dal层对象
public class MyTask : BackgroundService
{
private readonly CrontabSchedule _schedule;
private Timer? _timer;
private readonly ILogger<MyTask> _logger;
public MyTask(ILogger<MyTask> logger)
{
_logger = logger;
// 解析 CRON 表达式(使得NCrontab支持秒级定时)
_schedule = CrontabSchedule.Parse("5/5 * * * * *", new CrontabSchedule.ParseOptions { IncludingSeconds = true });
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
// 获取下一次执行的时间,并设置定时器
_timer = new Timer(DoWork, null, _schedule.GetNextOccurrence(DateTime.Now) - DateTime.Now, TimeSpan.FromMilliseconds(-1));
return Task.CompletedTask;
}
private void DoWork(object state)
{
// 执行任务
Console.WriteLine("执行每日任务: " + DateTime.Now);
// 重新计算下次执行时间,并重新设置定时器
// 通过不断设置定时器的方式替代Timer的period参数来实现循环
_timer.Change(_schedule.GetNextOccurrence(DateTime.Now) - DateTime.Now, TimeSpan.FromMilliseconds(-1));
}
public override void Dispose()
{
base.Dispose();
_timer?.Dispose();
}
}