项目中需要用到QuartZ执行定时任务,在此记录一下学习过程。
Quartz安装
在VS2022中,通过Nuget包管理器安装Quartz 3.8.1 ,这是.net 6 依赖的最高版本。
创建定时器任务
1、创建QuartzConfigurator
新建QuartzConfiguratorExtensions类,用于注册触发器和任务,代码如下:
/// <summary> /// 添加任务和触发器 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="quartz"></param> /// <param name="config"></param> /// <exception cref="Exception"></exception> public static void AddJobAndTrigger<T>(this IServiceCollectionQuartzConfigurator quartz, IConfiguration config) where T : IJob { // Use the name of the IJob as the appsettings.json key string jobName = typeof(T).Name; // Try and load the schedule from configuration var configKey = $"Quartz:{jobName}"; var cronSchedule = config[configKey]; // Some minor validation if (string.IsNullOrEmpty(cronSchedule)) { throw new Exception($"No Quartz.NET Cron schedule found for job in configuration at {configKey}"); } // register the job as before var jobKey = new JobKey(jobName); quartz.AddJob<T>(opts => opts.WithIdentity(jobKey)); quartz.AddTrigger(opts => opts .ForJob(jobKey) .WithIdentity(jobName + "-trigger") .WithCronSchedule(cronSchedule)); // use the schedule from configuration } /// <summary> /// 添加任务和触发器(带参数传递) /// </summary> /// <typeparam name="T"></typeparam> /// <param name="quartz"></param> /// <param name="config"></param> /// <param name="keyValuePairs">需要传递的参数</param> /// <param name="IsTriggerJobDataMap">默认通过 工作描述时传递参数</param> /// <exception cref="Exception"></exception> public static void AddJobAndTriggerWithParameter<T>(this IServiceCollectionQuartzConfigurator quartz, IConfiguration config, IDictionary<string, object>? keyValuePairs = null, bool isJobDetailJobDataMap = true) where T : IJob { // Use the name of the IJob as the appsettings.json key string jobName = typeof(T).Name; // Try and load the schedule from configuration var configKey = $"Quartz:{jobName}"; var cronSchedule = config[configKey]; // Some minor validation if (string.IsNullOrEmpty(cronSchedule)) { throw new Exception($"No Quartz.NET Cron schedule found for job in configuration at {configKey}"); } // register the job as before var jobKey = new JobKey(jobName); if (keyValuePairs != null && isJobDetailJobDataMap) { switch (isJobDetailJobDataMap) { case true: quartz.AddJob<T>(opts => opts .WithIdentity(jobKey) .UsingJobData(new JobDataMap(keyValuePairs))); quartz.AddTrigger(opts => opts .ForJob(jobKey) .WithIdentity(jobName + "-trigger") .WithCronSchedule(cronSchedule)); // use the schedule from configuration break; case false: quartz.AddJob<T>(opts => opts .WithIdentity(jobKey)); quartz.AddTrigger(opts => opts .ForJob(jobKey) .WithIdentity(jobName + "-trigger") .WithCronSchedule(cronSchedule) .UsingJobData(new JobDataMap(keyValuePairs))); // use the schedule from configuration break; } } else { quartz.AddJob<T>(opts => opts .WithIdentity(jobKey)); quartz.AddTrigger(opts => opts .ForJob(jobKey) .WithIdentity(jobName + "-trigger") .WithCronSchedule(cronSchedule)); // use the schedule from configuration } }
2、在Program.cs 中注入服务
builder.Services.AddQuartz(q => { 创建计划单元(时间轴,载体) //StdSchedulerFactory schedulerFactory = new StdSchedulerFactory(); //var scheduler = await schedulerFactory.GetScheduler(); //await scheduler.Start(); q.UseMicrosoftDependencyInjectionJobFactory(); // Register the job, loading the schedule from configuration q.AddJobAndTrigger<FromKingdeeWorkerJob>(builder.Configuration); }); builder.Services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);
3、创建工作单元WorkerJob
新建类TestWorkerJob,并继承IJob,代码如下:
[PersistJobDataAfterExecution]//在执行完成后,保留JobDataMap数据 [DisallowConcurrentExecution]//不允许并发执行,即必须等待上次完成后才能执行下一次 public class TestWorkerJob : IJob { private readonly ILogger<TesteWorkerJob> _logger; public TestWorkerJob(ILogger<TestWorkerJob> logger) { _logger = logger; } public Task Execute(IJobExecutionContext context) { _logger.LogInformation(DateTime.Now +" --- Hello world!"); Task.Delay(50000); Thread.Sleep(10000); return Task.CompletedTask; } }
假如我们的定时任务,执行一次需要耗时比较久,而且后一次执行需要等待前一次完成,并且需要前一次执行的结果作为参考,那么就需要设置任务的任性。因为默认情况下,工作单元在每一次运行都是一个新的实例,相互之间独立运行,互不干扰。所以如果需要存在一定的关联,就要设置任务的特性,主要有两个,如下所示:
[PersistJobDataAfterExecution]//在执行完成后,保留JobDataMap数据
[DisallowConcurrentExecution]//不允许并发执行,即必须等待上次完成后才能执行下一次
以上两个特性,只需要标记在任务对应的类上即可。
4、appsettings.json配置
在appsettings.json文件中添加一项Quartz,子项的必须与WorkerJob的名字保持一致,value是Cron表达式
{ "Quartz": { "FromKingdeeWorkerJob": "0/5 * * * * ?" } }
然后,启动项目,就可以看到任务可以正常运行啦。
最后
最后附上学习链接,