.Net Core(五) 引入定时任务Quartz.Net
一、前言
近期项目中遇到一些需求,需要定时写入数据库,定时刷新缓存的问题,因此需要引入任务调度机制。
我的选择是使用 Quartz.Net,使用的版本是 3.2.4
这里强调一点:3.x的版本与2.x的版本使用方式有一定的差别,需要注意一下!!!
什么是Quartz.NET? Quartz.NET官方文档
Quartz.NET 是一个功能齐全的开源作业调度系统,可用于从最小的应用程序到大型企业系统。
二、Quartz.Net机制图
三、.Net Core中引入Quartz
新建.Net Core 类库项目命名为 MCronJob
创建完成后引入Quartz.Net
在Package Manager Console输入如下命令 安装Quartz包
Install-Package Quartz
由Quartz.Net关系图可知,我们需要JobFactory和实际任务类HelloJob
创建 CronJobFactory 类并实现 IJobFactory接口 ------------------任务工厂
public class CronJobFactory : IJobFactory { public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) { throw new NotImplementedException(); } public void ReturnJob(IJob job) { throw new NotImplementedException(); } }
这里打个问号?NewJob和ReturnJob两个方法用来做什么?---------我是伏笔①
创建HelloJob类并实现 IJob接口 ---------------------------自定义的实际任务
public class HelloJob : IJob { public async Task Execute(IJobExecutionContext context) { await Console.Out.WriteLineAsync($"{DateTime.Now:HH:mm:ss}--Hello World!"); } }
有了任务工厂和任务,我们接下来实现具体的任务调度
新建SchedulerCenter类 ---------------------------------------------调度中心
public class SchedulerCenter { private readonly IJobFactory _jobFactory; private readonly ISchedulerFactory _schedulerFactory; private IScheduler _scheduler; public SchedulerCenter(IJobFactory jobFactory, ISchedulerFactory schedulerFactory) { _jobFactory = jobFactory; _schedulerFactory = schedulerFactory; } public async void StartScheduler() { //1、从工厂获取调度程序实例 _scheduler = await _schedulerFactory.GetScheduler(); // 替换默认工厂 //_scheduler.JobFactory = this._jobFactory; //2、打开调度器 await _scheduler.Start(); //3、定义作业详细信息并将其与HelloJob任务相关联 IJobDetail job = JobBuilder.Create<HelloJob>() .WithIdentity("HelloJob", "HelloJobGroup") .Build(); //4、配置触发条件:立即触发作业运行,然后每10秒重复一次 ITrigger trigger = TriggerBuilder.Create() .WithIdentity("HelloJob", "HelloJobGroup") .StartNow() .WithSimpleSchedule(x => x .WithIntervalInSeconds(10) .RepeatForever()) .Build(); //5、将作业与触发条件添加到调度实例并进行关联 await _scheduler.ScheduleJob(job, trigger); } public void StopScheduler() { _scheduler?.Shutdown(true).Wait(30000); _scheduler = null; } }
SchedulerCenter 中定义了两个方法 StartScheduler(开启调度)和StopScheduler(停止调度)
StartScheduler方法中关于任务配置与官网相同,这里不再赘述 Quartz.Net官方配置
这里注掉了一段代码 替换默认工厂 ---------我是伏笔②
接下来我们要启动这个定时任务,在Startup类的ConfigureServices方法中进行注入
//注入调度中心 services.AddSingleton<SchedulerCenter>(); //注入Quartz任何工厂及调度工厂 services.AddSingleton<IJobFactory, CronJobFactory>(); services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>(); //注入HelloJob services.AddTransient<HelloJob>();
在Startup类的Configure方法中进行启动配置
//获取调度中心实例 var quartz = app.ApplicationServices.GetRequiredService<SchedulerCenter>(); lifetime.ApplicationStarted.Register(() => { quartz.StartScheduler(); //项目启动后启动调度中心 }); lifetime.ApplicationStopped.Register(() => { quartz.StopScheduler(); //项目停止后关闭调度中心 });
运行截图:
成功启动定时任务
其他方式启动定时任务: Net Core 官方使用方式
四、Job实现服务注入
HelloJob类中注入用户信息服务,输出用户信息,改动如下:
public class HelloJob : IJob { private readonly IUserInfoServices _userInfoServices; public HelloJob(IUserInfoServices userInfoServices) { _userInfoServices = userInfoServices; } public async Task Execute(IJobExecutionContext context) { var userInfo = _userInfoServices.GetUserInfo(); await Console.Out.WriteLineAsync($"{DateTime.Now:HH:mm:ss}--姓名:{userInfo.UserName},年龄:{userInfo.Age},地址:{userInfo.Address}"); } }
再次运行项目,等待了一段时间发现并没有我们想要的输出(这里折腾我好久。。。)
这时回看刚刚埋下的伏笔① ,IJobFactory接口中有两个方法,NewJob和ReturnJob,这俩个方法做什么用?
NewJob方法: 注解的大概意思是:当触发器触发时获取一个Job实例供调度器执行,那么如何获取的Job实例呢?
我们看下NewJob第一个参数 TriggerFiredBundle,转到TriggerFiredBundle定义,看其为我们提供了什么。
如上图所示:TriggerFiredBundle中可以获取到IJobDetail
IJobDetail中有JobType属性也就是我们定义的Job类型,我们要做的就是获取到Job实例
安装 Microsoft.AspNetCore.Antiforgery包
Install-Package Microsoft.AspNetCore.Antiforgery -Version 2.2.0
改动Job工厂类如下所示:
public class CronJobFactory : IJobFactory { private readonly IServiceProvider _serviceProvider; public CronJobFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); } public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) { return _serviceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob; } public void ReturnJob(IJob job) { } }
接着打开SchedulerCenter中 伏笔② 的注释:替换默认工厂
// 替换默认工厂 _scheduler.JobFactory = this._jobFactory;
再次运行,用户服务成功调用
猜测:替换默认任务工厂后 IServiceProvider 取到的HelloJob是 ConfigureServices 中使用依赖注入的方式取到的实例,可取到UserInfoServices;
而未替换默认任务工厂取到的HelloJob并不能取到UserInfoServices,故HelloJob的Execute不能执行。