十七、.net core(.NET 6)搭建基于Quartz组件的定时调度任务
搭建基于Quartz组件的定时调度任务
先在package包项目下,添加Quartz定时器组件:
新建类库项目Wsk.Core.QuartzNet,并且引用包类库项目。然后新建一个中间调度类,叫QuartzMiddleJob:
中间Job源码:
public class QuartzMiddleJob : IJob { private readonly IServiceProvider _serviceProvider; public QuartzMiddleJob(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public async Task Execute(IJobExecutionContext context) { using (var scope = _serviceProvider.CreateScope()) { var jobType = context.JobDetail.JobType; var job = scope.ServiceProvider.GetRequiredService(jobType) as IJob; await job.Execute(context); } } }
View Code
新建一个Job工厂类,叫WeskyJobFactory,用来获取刚刚创建的中间调度类的服务:
新建一个通用执行计划类,叫WeskyJobSchedule,用于每次任务都通过该计划进行生成:
计划类和枚举源码:
public class WeskyJobSchedule { public WeskyJobSchedule(Type jobType, string cronExpression) { this.JobType = jobType ?? throw new ArgumentNullException(nameof(jobType)); CronExpression = cronExpression ?? throw new ArgumentNullException(nameof(cronExpression)); } /// <summary> /// Job类型 /// </summary> public Type JobType { get; private set; } /// <summary> /// Cron表达式 /// </summary> public string CronExpression { get; private set; } /// <summary> /// Job状态 /// </summary> public JobStatus JobStatu { get; set; } = JobStatus.Init; } /// <summary> /// 运行状态 /// </summary> public enum JobStatus : byte { [Description("Initialization")] Init = 0, [Description("Running")] Running = 1, [Description("Scheduling")] Scheduling = 2, [Description("Stopped")] Stopped = 3, }
View Code
现在添加一个任务,新建任务类 MyJobs,并且继承自IJob,然后在Excute方法里面,就是该任务执行调度时候会进去执行的了:
似乎上面哪儿感觉不太对,咱们把原来的Job工厂里面到代码稍微调整下如下:
NewJob源码:
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) { return _serviceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob; }
现在新增一个静态类QuartzJobService,用来当做调度任务的中间启动项,并且把有关的一些服务注册进来:
对应源码:
public static class QuartzJobService { public static void AddQuartzJobService(this IServiceCollection services) { if (services == null) { throw new ArgumentNullException(nameof(services)); } services.AddSingleton<IJobFactory, WeskyJobFactory>(); services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>(); services.AddSingleton<QuartzMiddleJob>(); services.AddSingleton<MyJobs>(); services.AddSingleton( new WeskyJobSchedule(typeof(MyJobs), "0/1 * * * * ? ") ); services.AddHostedService<WeskyJobHostService>(); } }
最后,还少了个启动项,用来程序启动的时候,进行启动定时调度任务。新建类 WeskyJobHostService ,并且新建创建调度任务方法 CreateJob和触发器方法CreateTrigger:
然后,在开始和结束方法内:
以上源码如下:
public class WeskyJobHostService: IHostedService { private readonly ISchedulerFactory _schedulerFactory; private readonly IJobFactory _jobFactory; private readonly IEnumerable<WeskyJobSchedule> _jobSchedules; public WeskyJobHostService(ISchedulerFactory schedulerFactory, IJobFactory jobFactory, IEnumerable<WeskyJobSchedule> jobSchedules) { _schedulerFactory = schedulerFactory ?? throw new ArgumentNullException(nameof(schedulerFactory)); _jobFactory = jobFactory ?? throw new ArgumentNullException(nameof(jobFactory)); _jobSchedules = jobSchedules ?? throw new ArgumentNullException(nameof(jobSchedules)); } public IScheduler Scheduler { get; set; } public async Task StartAsync(CancellationToken cancellationToken) { Scheduler = await _schedulerFactory.GetScheduler(cancellationToken); Scheduler.JobFactory = _jobFactory; foreach (var jobSchedule in _jobSchedules) { var job = CreateJob(jobSchedule); var trigger = CreateTrigger(jobSchedule); await Scheduler.ScheduleJob(job, trigger, cancellationToken); jobSchedule.JobStatu = JobStatus.Scheduling; } await Scheduler.Start(cancellationToken); foreach (var jobSchedule in _jobSchedules) { jobSchedule.JobStatu = JobStatus.Running; } } public async Task StopAsync(CancellationToken cancellationToken) { await Scheduler?.Shutdown(cancellationToken); foreach (var jobSchedule in _jobSchedules) { jobSchedule.JobStatu = JobStatus.Stopped; } } private static IJobDetail CreateJob(WeskyJobSchedule schedule) { var jobType = schedule.JobType; return JobBuilder .Create(jobType) .WithIdentity(jobType.FullName) .WithDescription(jobType.Name) .Build(); } private static ITrigger CreateTrigger(WeskyJobSchedule schedule) { return TriggerBuilder .Create() .WithIdentity($"{schedule.JobType.FullName}.trigger") .WithCronSchedule(schedule.CronExpression) .WithDescription(schedule.CronExpression) .Build(); } }
View Code
切回QuartzJobService,在 AddQuartzJobService 方法的最下方,添加上面启动服务的注册:
最后,在启动项目里面,添加对Wsk.CoreQuartz项目的引用,然后在WskService服务类下,添加对AddQuartzJobService服务的注册:
启动项目,看看效果:
由此可见,我们设置的每秒一次触发效果达成。为了检验是不是可以避免同一个调度任务产生并发,在调度任务方法里面,设置一个延时,看看效果:
运行结果:
说明在当前任务还没有完成的情况下,不会重复进入。如果要允许重复进,只需要把类上面的DisallowConcurrentExecution 标签注释掉就可以。
现在还原回去,然后在Cron表达式改写成定时10秒,看看效果:
运行结果:
以上就是本篇使用QuartzNet的全部内容,仅用于入门参考。对于其他定时用法、以及各种比较飘的使用,各位大佬可以自行变种。如果有什么建议或意见,也欢迎留言~~