Quartz NetCore定时器任务应用之基于Quartz
Quartz Net 是一个强大,开源,轻量的作业调度框架,可以创建简单或复杂的作业调度来执行一个Task。
Quartz主要由3部分组成:
Scheduler:调度器,根据Trigger中设置调用周期执行Job。
Trigger:触发器,设置Job执行周期。
Job:具体需要执行的业务。
本篇介绍Quartz在netcore中的一种应用纯Quartz类库开发netcore定时器任务,下篇会介绍通过Quartz.Extensions.Hosting类库更简便的开发基于netcore的定时器任务
1、Quartz,通过Manage NuGet Pakage / Pakage Manage Console 安装Quartz,当前最新版本3.6.2
2、创建2个任务(IJob)ServiceJobA,ServiceJobB分别设置DisallowConcurrentExecution attribute,阻止并发调用
[DisallowConcurrentExecution] public class ServiceJobA : IJob { private readonly ILogger<ServiceJobA> _logger; public ServiceJobA(ILogger<ServiceJobA> logger) { _logger = logger; } public Task Execute(IJobExecutionContext context) { Task.Run(() => { _logger.LogInformation($"DateTime.Now: {DateTime.Now.ToLongTimeString()}, The service job A has been excuted."); }); return Task.CompletedTask; } }
using Quartz; namespace API.Quartz.QuartzV2; [DisallowConcurrentExecution] public class ServiceJobB : IJob { private readonly ILogger<ServiceJobA> _logger; public ServiceJobB(ILogger<ServiceJobA> logger) { _logger = logger; } public Task Execute(IJobExecutionContext context) { Task.Run(() => { _logger.LogInformation($"DateTime.Now: {DateTime.Now.ToLongTimeString()}, The service job B has been excuted."); }); return Task.CompletedTask; } }
3、创建QuartzJobFactory,目的是通过ServiceProvider从容器中获取IJob,可以参考Quartz源码中Quartz.Simpl.SimpleJobFactory,创建自定义JobFactory之后就可以给调度器(Scheduler)指定自定义的JobFactory。
using Quartz; using Quartz.Spi; namespace API.Quartz.QuartzV2 { public class QuartzJobFactory : IJobFactory { private readonly IServiceProvider _serviceProvider; public QuartzJobFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) { return _serviceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob; } /// <summary> /// Allows the job factory to destory/cleanup the job if needed /// </summary> /// <param name="job"></param> public void ReturnJob(IJob job) { var disposable = job as IDisposable; disposable?.Dispose(); } } }
通过ServcieProvider从IOC容器获取IJob,IJob的实例就需要添加到IOC容器中,从而在IJob的实例可以通过构造函数的方式实现依赖注入,比如缓存类,日志类,及第三方或自定义的service服务。
4、创建HostService,轮询执行IJob
using Quartz; using Quartz.Spi; namespace API.Quartz.QuartzV2 { public class QuartzHostedService : IHostedService { private readonly ISchedulerFactory _schedulerFactory; private readonly IJobFactory _jobFactory; private readonly List<JobScheduler> _jobSchedulers; public QuartzHostedService(ISchedulerFactory schedulerFactory, IJobFactory jobFactory, List<JobScheduler> jobSchedulers) { _schedulerFactory = schedulerFactory ?? throw new ArgumentNullException(nameof(schedulerFactory)); _jobFactory = jobFactory ?? throw new ArgumentNullException(nameof(jobFactory)); _jobSchedulers = jobSchedulers ?? throw new ArgumentNullException(nameof(jobSchedulers)); } private IScheduler _scheduler { get; set; } public async Task StartAsync(CancellationToken cancellationToken) { _scheduler = await _schedulerFactory.GetScheduler(cancellationToken); _scheduler.JobFactory = _jobFactory; foreach (var jobScheduler in _jobSchedulers) { var job = CreateJob(jobScheduler); var trigger = CreateTrigger(jobScheduler); await _scheduler.ScheduleJob(job, trigger, cancellationToken); } await _scheduler.Start(cancellationToken); } public async Task StopAsync(CancellationToken cancellationToken) { await _scheduler.Shutdown(cancellationToken); } private static IJobDetail CreateJob(JobScheduler schedule) => JobBuilder.Create(schedule.JobType).WithIdentity(schedule.JobType.Name).WithDescription(schedule.JobType.FullName).Build(); private static ITrigger CreateTrigger(JobScheduler schedule) => TriggerBuilder.Create().WithIdentity($"{schedule.JobType.Name}.trigger").WithCronSchedule(schedule.CronString).WithDescription(schedule.CronString).Build(); } }
_scheduler = await _schedulerFactory.GetScheduler(cancellationToken);
这里之所以能用构造函数依赖注入的_schedulerFactory是因为在IOC容器中注入了StdSchedulerFactory
builder.Services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();
获取到调度器(Scheduler)之后,给Scheduler指定JobFactory,及以上创建的QuartzJobFactory
_scheduler.JobFactory = _jobFactory;
5、为了调用方便,这里又定义了一个Job和Trigger的关系类JobTrigger,指定JobType及Cron表达式
namespace API.Quartz.QuartzV2; public class JobTrigger { public Type JobType { get; private set; } public String CronString { get; private set; } public JobTrigger(Type jobType, string cronString) { JobType = jobType; CronString = cronString; } }
6、在IOC容器中注入StdSchedulerFactory, ServiceJobA,ServiceJobB及初始化一系列JobTrigger任务串,最后向IOC中注入了自定义的QuartzHostedService
var config = builder.Configuration; builder.Services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>(); builder.Services.AddTransient<API.Quartz.QuartzV2.ServiceJobA>(); builder.Services.AddTransient<API.Quartz.QuartzV2.ServiceJobB>(); builder.Services.AddSingleton<IJobFactory, QuartzJobFactory>(); builder.Services.AddSingleton(sp => new List<JobTrigger> { new JobTrigger(typeof(API.Quartz.QuartzV2.ServiceJobA), config[$"Quartz:{typeof(API.Quartz.QuartzV2.ServiceJobA).Name}"]), new JobTrigger(typeof(API.Quartz.QuartzV2.ServiceJobB), config[$"Quartz:{typeof(API.Quartz.QuartzV2.ServiceJobB).Name}"]) }); builder.Services.AddHostedService<QuartzHostedService>();
7、appsettings.json 配置了每个IJob 调用的周期Cron
"Quartz": {
"ServiceJobA": "0/5 * * * * ?",
"ServiceJobB": "0/30 * * * * ?"
}
工程文件目录
启动程序,效果如下:
JobServiceA每5s执行一次,JobServiceB每30s执行一次。