十年河东,十年河西,莫欺少年穷
学无止境,精益求精
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表达式生成器:
https://www.toolzl.com/tools/croncreate.html
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、新建控制台应用程序
项目清单如下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<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、简单时间间隔作业
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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】防并发作业)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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副本)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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表达式生成器:
https://www.toolzl.com/tools/croncreate.html
每月最后一天23:59分执行
日部分截图
小时部分截图
分钟部分截图
秒部分截图
最终生成的Corn 表达式为:0 59 23 L * ? *
示例代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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();
@天才卧龙的博科人、
标签:Quartz,AspNetCore,调度,Job,3.5,Extensions,var,using,public From: https://www.cnblogs.com/chenwolong/p/NetCoreQuartz.html