Net6 定时调度Quartz.AspNetCore(3.5.0)的使用

十年河东,十年河西,莫欺少年穷

学无止境,精益求精

1、概述

Quartz.Net是根据Java的Quartz用C#改写而来,Quartz.NET是一个开源的作业调度框架,非常适合在平时的工作中,定时轮询数据库同步,定时邮件通知,定时处理数据等。 Quartz.NET允许开发人员根据时间间隔来调度作业。它有很多特征如:数据库支持,集群,插件,支持cron-like表达式等等。

2、参考

Quartz.Net源码:https://github.com/quartznet/quartznet

官方学习文档:http://www.quartz-scheduler.net/documentation/index.html

3、Quartz.Net说明

Quartz主要有三部分组成任务(Job)、触发器(Trigger)和调度器(Schedule)。

3.1、作业

Job就是执行的作业,Job需要继承IJob接口,实现Execute方法。Job中执行的参数从Execute方法的参数中获取。

3.2、触发器

触发器常用的有两种:SimpleTrigger触发器和CronTrigger触发器。

SimpleTrigger

 实现简单业务,如每隔几分钟,几小时触发执行,并限制执行次数。

  • 重复执行:WithRepeatCount()/RepeatForever()
  • 设置间隔时间:WithInterval()
  • 定时执行:StartAt()/StartNow()
  • 设定优先级:WithPriority(),默认为5
var trigger = TriggerBuilder.Create()
                       .WithSimpleSchedule(x => x.WithIntervalInSeconds(2).WithRepeatCount(5))//间隔2秒 执行6次
                       .UsingJobData("key1", 321)
                       .WithIdentity("trigger", "group")
                       .Build();

CronTrigger

CornTrigger需要接合Corn表达式

常用cron表达式

  • */10 * * * * ? 每隔10秒执行一次
  • 0 */5 * * * ? 每隔5分钟执行一次
  • 0 2,22,32 * * * ? 在2分、22分、32分执行一次
  • 0 0 4-8 * * ? 每天4-8点整点执行一次
  • 0 0 2 * * ? 每天凌晨2点执行一次
  • 0 0 2 1 * ? 每月1号凌晨2点执行一次

cron表达式生成器

现在也有许多在线的cron表达式生成器:

            var trigger = TriggerBuilder.Create().StartNow()
                            .WithCronSchedule("0/2 * * * * ? *")//每两秒执行一次
                            .Build();

3.3、调度器

调度器就是将任务和触发器绑定,让触发器触发的时候去执行任务。

3.4、参数说明

  • SetJobData:设置JobData

  • StoreDurably:孤立存储,指即使该JobDetail没有关联的Trigger,也会进行存储

  • RequestRecovery:请求恢复,指应用崩溃后再次启动,会重新执行该作业

  • WithIdentity:作业的唯一标识

  • WithDescription:作业的描述信息

除此之外,Quartz.Net还支持两个非常有用的特性:

  • DisallowConcurrentExecution:禁止并行执行,该特性是针对JobDetail生效的

  • PersistJobDataAfterExecution:在执行完成后持久化JobData,该特性是针对Job类型生效的,意味着所有使用该Job的JobDetail都会在执行完成后持久化JobData。

4、新建控制台应用程序

项目清单如下

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
    <PackageReference Include="Quartz.AspNetCore" Version="3.5.0" />
      <PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" />
      <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
      <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
      <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
      <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
      <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
      <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
      <PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
      <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
      <PackageReference Include="NLog.Extensions.Logging" Version="5.0.4" />
  </ItemGroup>

  <ItemGroup>
    <None Update="appsettings.json">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </None>
  </ItemGroup>

</Project>
View Code

4.1、简单时间间隔作业

using Newtonsoft.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NLog.Extensions.Logging;
using Quartz;
using Quartz.Impl;
using Quartz.Logging;
using System;

namespace BatteryService // Note: actual namespace depends on the project name.
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            ConfigurationBuilder builder = new ConfigurationBuilder();
            builder.AddJsonFile("appsettings.json", true, true);
            var ConfigRoot = builder.Build();//根节点
            IServiceCollection Services = new ServiceCollection(); 
            // 
            Services.AddLogging(log => { log.AddConsole(); log.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Information); }); 
            Services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();//注册ISchedulerFactory的实例。
            Services.AddScoped<QuartzService>();//注册 QuartzService 的实例。
            using (ServiceProvider provider = Services.BuildServiceProvider())
            {
                var service = provider.GetService<QuartzService>();
                await service.with2Seconds();
            };
            CreateHostBuilder(args).Run();
        }

        public static IHost CreateHostBuilder(string[] args)
        {
            var builder = Host.CreateDefaultBuilder(args)
                 .ConfigureServices((hostContext, services) =>
                 {
                     //services.AddHostedService<IotService>();
                 }).UseWindowsService();
            var host = builder.Build();

            return host;
        }
    }

    public class QuartzService
    { 
        private readonly ISchedulerFactory _schedulerFactory; 
        private IScheduler _scheduler;
        private readonly ILogger<QuartzService> logger;
        public QuartzService(ISchedulerFactory schedulerFactory, ILogger<QuartzService> logger)
        {
            _schedulerFactory = schedulerFactory;
            this.logger = logger;
        }

        public async Task with2Seconds()
        {
          
            //通过调度工厂获得调度器
            _scheduler = await _schedulerFactory.GetScheduler(); 
            //开启调度器
            await _scheduler.Start();
            logger.LogInformation("调度器开始工作");
            //创建一个触发器
            var trigger = TriggerBuilder.Create().StartNow()
                            .WithSimpleSchedule(x => x.WithIntervalInSeconds(2).RepeatForever())//每两秒执行一次
                            .Build();
            //创建任务
            var jobDetail = JobBuilder.Create<MyJob>()
                            .WithIdentity("job1", "group")
                            .Build();
            //将触发器和任务器绑定到调度器中
            await _scheduler.ScheduleJob(jobDetail, trigger);
        }
    }
    [DisallowConcurrentExecution]//禁止并行执行,该特性是针对JobDetail生效的
    public class MyJob : IJob
    {  
        public Task Execute(IJobExecutionContext context)
        {
            return Task.Run(() =>
            {
                Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); 
            });
        }
    } 
}  
View Code

 4.2、可以传递参数的简单作业(使用了特性【DisallowConcurrentExecution】防并发作业)

using Newtonsoft.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NLog.Extensions.Logging;
using Quartz;
using Quartz.Impl;
using Quartz.Logging;
using System;

namespace BatteryService // Note: actual namespace depends on the project name.
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            ConfigurationBuilder builder = new ConfigurationBuilder();
            builder.AddJsonFile("appsettings.json", true, true);
            var ConfigRoot = builder.Build();//根节点
            IServiceCollection Services = new ServiceCollection(); 
            // 
            Services.AddLogging(log => { log.AddConsole(); log.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Information); }); 
            Services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();//注册ISchedulerFactory的实例。
            Services.AddScoped<QuartzService>();//注册 QuartzService 的实例。
            using (ServiceProvider provider = Services.BuildServiceProvider())
            {
                var service = provider.GetService<QuartzService>();
                await service.with2Seconds();
            };
            CreateHostBuilder(args).Run();
        }

        public static IHost CreateHostBuilder(string[] args)
        {
            var builder = Host.CreateDefaultBuilder(args)
                 .ConfigureServices((hostContext, services) =>
                 {
                     //services.AddHostedService<IotService>();
                 }).UseWindowsService();
            var host = builder.Build();

            return host;
        }
    }

    public class QuartzService
    { 
        private readonly ISchedulerFactory _schedulerFactory; 
        private IScheduler _scheduler;
        private readonly ILogger<QuartzService> logger;
        public QuartzService(ISchedulerFactory schedulerFactory, ILogger<QuartzService> logger)
        {
            _schedulerFactory = schedulerFactory;
            this.logger = logger;
        }

        public async Task with2Seconds()
        {
          
            //通过调度工厂获得调度器
            _scheduler = await _schedulerFactory.GetScheduler(); 
            //开启调度器
            await _scheduler.Start();
            logger.LogInformation("调度器开始工作");
            //创建一个触发器
            var trigger = TriggerBuilder.Create().StartNow().UsingJobData("Trigger", "我是在Trigger中设置的参数").UsingJobData("TriggerParmCount", 1)
                            .WithSimpleSchedule(x => x.WithIntervalInSeconds(2).WithRepeatCount(1))//每两秒执行一次
                            .Build();
            //创建任务
            var jobDetail = JobBuilder.Create<MyJob>()
                .UsingJobData("Job_1", "我是在Job中设置的参数")
                .UsingJobData("Job_2", "我是在Job中设置的参数")
                .UsingJobData("ParmCount", 2)
                .WithIdentity("jobName", "group")
                .Build();
            //将触发器和任务器绑定到调度器中
            await _scheduler.ScheduleJob(jobDetail, trigger);
        }
    }
    [DisallowConcurrentExecution]//禁止并行执行,该特性是针对JobDetail生效的
    public class MyJob : IJob
    {  
        public Task Execute(IJobExecutionContext context)
        {
            var jobData = context.JobDetail.JobDataMap;//获取Job中的参数 
            var triggerData = context.Trigger.JobDataMap;//获取Trigger中的参数
            var allData = context.MergedJobDataMap;//获取Job和Trigger中合并的参数
            var Trigger = triggerData.GetString("Trigger");
            int TriggerParmCount = triggerData.GetInt("TriggerParmCount");

            var Job_1 = triggerData.GetString("Job_1");
            var Job_2 = triggerData.GetString("Job_2");
            int ParmCount = triggerData.GetInt("ParmCount");

            return Task.Run(() =>
            {
                foreach (var item in jobData)
                {
                    Console.WriteLine($"Job中的参数,键为:{item.Key}值为:{item.Value}");
                }
                Console.WriteLine("-----------------------------------------------");
                foreach (var item in triggerData)
                {
                    Console.WriteLine($"Trigger中的参数,键为:{item.Key}值为:{item.Value}");
                }
                Console.WriteLine("-----------------------------------------------");
                foreach (var item in allData)
                {
                    Console.WriteLine($"Job和Trigger中的所有参数,键为:{item.Key}值为:{item.Value}");
                }
            });
        }
    } 
}  
View Code

 4.3、可变参数简单作业(使用了特性【PersistJobDataAfterExecution】存储JobDataMap副本)

using Newtonsoft.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NLog.Extensions.Logging;
using Quartz;
using Quartz.Impl;
using Quartz.Logging;
using System;

namespace BatteryService // Note: actual namespace depends on the project name.
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            ConfigurationBuilder builder = new ConfigurationBuilder();
            builder.AddJsonFile("appsettings.json", true, true);
            var ConfigRoot = builder.Build();//根节点
            IServiceCollection Services = new ServiceCollection(); 
            // 
            Services.AddLogging(log => { log.AddConsole(); log.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Information); }); 
            Services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();//注册ISchedulerFactory的实例。
            Services.AddScoped<QuartzService>();//注册 QuartzService 的实例。
            using (ServiceProvider provider = Services.BuildServiceProvider())
            {
                var service = provider.GetService<QuartzService>();
                await service.with2Seconds();
            };
            CreateHostBuilder(args).Run();
        }

        public static IHost CreateHostBuilder(string[] args)
        {
            var builder = Host.CreateDefaultBuilder(args)
                 .ConfigureServices((hostContext, services) =>
                 {
                     //services.AddHostedService<IotService>();
                 }).UseWindowsService();
            var host = builder.Build();

            return host;
        }
    }

    public class QuartzService
    { 
        private readonly ISchedulerFactory _schedulerFactory; 
        private IScheduler _scheduler;
        private readonly ILogger<QuartzService> logger;
        public QuartzService(ISchedulerFactory schedulerFactory, ILogger<QuartzService> logger)
        {
            _schedulerFactory = schedulerFactory;
            this.logger = logger;
        }

        public async Task with2Seconds()
        {
          
            //通过调度工厂获得调度器
            _scheduler = await _schedulerFactory.GetScheduler(); 
            //开启调度器
            await _scheduler.Start();
            logger.LogInformation("调度器开始工作");
            //创建一个触发器
            var trigger = TriggerBuilder.Create().StartNow()
                .UsingJobData("name", "陈卧龙")
                .UsingJobData("sex", "")
                .WithSimpleSchedule(x => x.WithIntervalInSeconds(2).WithRepeatCount(9))//每两秒执行一次 共执行10次
                .Build();
            //创建任务
            var jobDetail = JobBuilder.Create<MyJob>()
                .UsingJobData("age", 28)
                .UsingJobData("weight", 70) 
                .WithIdentity("jobName", "group")
                .Build();
            //将触发器和任务器绑定到调度器中
            await _scheduler.ScheduleJob(jobDetail, trigger);
        }
    }
    [PersistJobDataAfterExecution]// 更新JobDetail的JobDataMap的存储副本,以便下一次执行这个任务接收更新的值而不是原始存储的值
    public class MyJob : IJob
    {  
        public Task Execute(IJobExecutionContext context)
        { 
            var allData = context.MergedJobDataMap;//获取Job和Trigger中合并的参数
            var name = allData.GetString("name");
            var sex = allData.GetString("sex"); 
            var age = allData.GetInt("age");
            var weight = allData.GetInt("weight");
            Console.WriteLine("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
            Console.WriteLine($"原始参数为,姓名:{name},性别:{sex},年龄:{age},体重:{weight}");

            allData["name"] = name + "_" + new Random().Next(1, 10);
            allData["sex"] = sex + "_" + new Random().Next(1, 10);
            allData["age"] = new Random().Next(20, 100);
            allData["weight"] = new Random().Next(60, 100);
            return Task.Run(() =>
            {  
                foreach (var item in allData)
                {
                    Console.WriteLine($"改变后的参数,键为:{item.Key}值为:{item.Value}");
                }
                Console.WriteLine("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
            });
        }
    } 
}  
View Code

 4.4、Corn表达式作业调度

corn表示式和简单调度的区别是:一个使用固定时间间隔作业,比较简单。一个可以通过corn表达式进行复杂的时间场景调度

现在也有许多在线的cron表达式生成器:

 

每月最后一天23:59分执行

日部分截图

 

 

 小时部分截图

 

 分钟部分截图

 

 秒部分截图

 

 

 最终生成的Corn 表达式为:0 59 23 L * ? *

示例代码如下:

        public async Task with2Seconds()
        {
          
            //通过调度工厂获得调度器
            _scheduler = await _schedulerFactory.GetScheduler(); 
            //开启调度器
            await _scheduler.Start();
            logger.LogInformation("调度器开始工作");
            //创建一个触发器
            var trigger = TriggerBuilder.Create().StartNow().WithCronSchedule("0 59 23 L * ? *") //每月月底23.59分执行
                .UsingJobData("name", "陈卧龙")
                .UsingJobData("sex", "")  
                .Build();
            //创建任务
            var jobDetail = JobBuilder.Create<MyJob>()
                .UsingJobData("age", 28)
                .UsingJobData("weight", 70) 
                .WithIdentity("jobName", "group")
                .Build();
            //将触发器和任务器绑定到调度器中
            await _scheduler.ScheduleJob(jobDetail, trigger);
        }
View Code

核心配置:

  var trigger = TriggerBuilder.Create().StartNow().WithCronSchedule("0 59 23 L * ? *") //每月月底23.59分执行
                .UsingJobData("name", "陈卧龙")
                .UsingJobData("sex", "")  
                .Build();

@天才卧龙的博科人、

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章