Quartz.NET 的应用

Quartz.NET 中学习了该框架的用法。
现需要基于该框架升级原本使用 Windows Service 实现的定时任务功能。
原功能使用自定义的系统任务表来保存定时任务的设置,而 Quartz.NET 则使用了一套自己的表,而且没有提供设置的 UI。

为了尽量减少修改量,所以实现的思想是尽量保留原本的任务增删改查功能。
而且经过调查发现,只需在原本处理的后面增加一个更新 Quart.NET 任务的代码就可以了。

下面是示例代码:

  1. 设置工程 QuartzSettingApplication

    在该工程中创建 Schedule 但是不启动,将定义好的 Job 增加到 Schedule 中之后就关闭掉该实例。这样修改的设置就可以自动更新到对应的数据库了(这里使用的是 SqlServer)。
    实际项目中只需要在原有的任务设置处理后面增加类似如下 Quartz 的设置处理就可以了。

    using Quartz;
    using Quartz.Impl;
    using Quartz.Logging;
    using QuartzJobs;
    using System;
    using System.Collections.Specialized;
    using System.Threading.Tasks;
    
    namespace QuartzSettingApplication
    {
        class Program
        {
            static void Main(string[] args)
            {
                LogProvider.SetCurrentLogProvider(new ConsoleLogProvider());
    
                // trigger async evaluation
                RunProgram().GetAwaiter().GetResult();
    
                Console.WriteLine("Press any key to close the application");
                Console.ReadKey();
            }
    
            private static async Task RunProgram()
            {
                // Grab the Scheduler instance from the Factory
                NameValueCollection props = new NameValueCollection
                    {
                        { "quartz.serializer.type", "binary" },
                        { "quartz.scheduler.instanceName", "MyScheduler" },
                        { "quartz.jobStore.type", "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz" },
                        { "quartz.jobStore.driverDelegateType", "Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz" },
                        { "quartz.jobStore.tablePrefix", "QRTZ_" },
                        { "quartz.jobStore.dataSource", "myDS" },
                        { "quartz.dataSource.myDS.connectionString", "Data Source=EDG2GYKVF8G5P6Z;Initial Catalog=Quartz;User ID=sa;Password=password;" },
                        { "quartz.dataSource.myDS.provider", "SqlServer" },
                        { "quartz.threadPool.threadCount", "3" },
                        { "quartz.scheduler.instanceId", "AUTO" },
                        { "quartz.jobStore.clustered", "true" },
                    };
    
                StdSchedulerFactory factory = new StdSchedulerFactory(props);
    
                IScheduler scheduler = await factory.GetScheduler();
                // 删除 Jobs
                for (int i = 0; i <= 2; i++)
                {
                    if (scheduler.CheckExists(new JobKey($"job{ i + 1}", "group1")).GetAwaiter().GetResult())
                    {
                        await scheduler.DeleteJob(new JobKey($"job{ i + 1}", "group1"));
                    }
                }
    
                await Task.Delay(TimeSpan.FromSeconds(10));
    
                // 重新添加 Jobs
                for (int i = 0; i <= 2; i++)
                {
                    if (!scheduler.CheckExists(new JobKey($"job{ i + 1}", "group1")).GetAwaiter().GetResult())
                    {
                        // define the job and tie it to our HelloJob class
                        IJobDetail job = JobBuilder.Create<HelloJob>()
                            .WithIdentity($"job{ i + 1}", "group1")
                            .UsingJobData("count", 2)
                            .Build();
    
                        // Trigger the job to run now, and then repeat every 10 seconds
                        ITrigger trigger = TriggerBuilder.Create()
                            .WithIdentity($"trigger{ i + 1}", "group1")
                            .StartNow()
                            .WithSimpleSchedule(x => x
                                .WithIntervalInSeconds(10)
                                //.WithRepeatCount(0)
                                .RepeatForever()
                                )
                            .Build();
    
                        // Tell quartz to schedule the job using our trigger
                        await scheduler.ScheduleJob(job, trigger);
                    }
                }
    
                await Task.Delay(TimeSpan.FromSeconds(10));
    
                await scheduler.Shutdown();
            }
        }
    }
    
  2. 执行 JOB 的工程 QuartzService

    这个是执行 Job 的工程,启动后永不关闭。这个工程可以开启多个实例。
    需要注意的是,这里启用了集群功能,需要保证开启的实例(包括上面设置 Job 时启动的实例)的设置是一样(除了 quartz.threadPool.threadCount),而且服务器系统时间不能有超过 1 秒的误差。

    using Quartz;
    using Quartz.Impl;
    using Quartz.Logging;
    using QuartzJobs;
    using System;
    using System.Collections.Specialized;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace QuartzService
    {
        class Program
        {
            static void Main(string[] args)
            {
                LogProvider.SetCurrentLogProvider(new ConsoleLogProvider());
    
                // trigger async evaluation
                RunProgram().GetAwaiter().GetResult();
            }
    
            private static async Task RunProgram()
            {
                try
                {
                    // Grab the Scheduler instance from the Factory
                    NameValueCollection props = new NameValueCollection
                    {
                        { "quartz.serializer.type", "binary" },
                        { "quartz.scheduler.instanceName", "MyScheduler" },
                        { "quartz.jobStore.type", "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz" },
                        { "quartz.jobStore.driverDelegateType", "Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz" },
                        { "quartz.jobStore.tablePrefix", "QRTZ_" },
                        { "quartz.jobStore.dataSource", "myDS" },
                        { "quartz.dataSource.myDS.connectionString", "Data Source=EDG2GYKVF8G5P6Z;Initial Catalog=Quartz;User ID=sa;Password=password;" },
                        { "quartz.dataSource.myDS.provider", "SqlServer" },
                        { "quartz.threadPool.threadCount", "3" },
                        { "quartz.scheduler.instanceId", "AUTO" },
                        { "quartz.jobStore.clustered", "true" },
                    };
    
                    StdSchedulerFactory factory = new StdSchedulerFactory(props);
    
                    //StdSchedulerFactory factory = new StdSchedulerFactory();
                    IScheduler scheduler = await factory.GetScheduler();
    
                    // and start it off
                    await scheduler.Start();
    
                    await Task.Delay(Timeout.Infinite);
    
                    // and last shut down the scheduler when you are ready to close your program
                    await scheduler.Shutdown();
                }
                catch (SchedulerException se)
                {
                    await Console.Error.WriteLineAsync(se.ToString());
                }
            }
        }
    }
    
Quartz.NET

Quartz.NET 是一个定时计划任务的框架,支持 .NET Core。

本文示例代码大部分来自于官方教程:Quartz.NET - Quartz.NET 3.x Tutorial

Target Framework:.NET Core 2.2
Quartz:3.0.7