定时调度之Quartz
工作中我们经常碰到定时或者固定时间点去做一些事情,然后每天到时间点就会去做这样的事情,如果理解这样的场景,我们就要引入今天我们的主角Quartz,其实这个跟数据库的作业类似,但是不仅仅局限于数据库。
一: quartZ引入&三大核心对象简介
1:在项目中打开Nuget管理,然后搜索QuartZ,现在最新的版本是3.0.7,需要在Framework4.5.2上面使用。
2:quartZ的三大核心对象
A:IScheduler:单元/实例,在这里去完成定时任务的配置,只有单元启动,里面的作业才能正常运行
B:IJob:任务,定时执行动作就是Job
C:ITrigger:定时策略(设置执行的频率或者执行方式)
二:三大核心对象的初始化以及使用如下:
1 #region scheduler 2 Console.WriteLine("初始化scheduler......"); 3 StdSchedulerFactory factory = new StdSchedulerFactory(); 4 IScheduler scheduler = await factory.GetScheduler(); 5 //scheduler.ListenerManager.AddSchedulerListener(new CustomSchedulerListener()); 6 //scheduler.ListenerManager.AddTriggerListener(new CustomTriggerListener()); 7 //scheduler.ListenerManager.AddJobListener(new CustomJobListener()); 8 await scheduler.Start(); 9 #endregion 10 11 //IJob ITrigger 12 { 13 //创建作业 14 IJobDetail jobDetail = JobBuilder.Create<TestJob>() 15 .WithIdentity("testjob", "group1") 16 .WithDescription("This is TestJob") 17 .Build(); 18 19 //IJobDetail jobDetail = JobBuilder.Create<TestStatefulJob>() 20 // .WithIdentity("testjob", "group1") 21 // .WithDescription("This is TestJob") 22 // .Build(); 23 24 jobDetail.JobDataMap.Add("student1", "Milor"); 25 jobDetail.JobDataMap.Add("student2", "心如迷醉"); 26 jobDetail.JobDataMap.Add("student3", "宇洋"); 27 jobDetail.JobDataMap.Add("Year", DateTime.Now.Year); 28 29 //ITrigger trigger = TriggerBuilder.Create() 30 // .WithIdentity("trigger1", "group1") 31 // .StartNow() 32 // .WithSimpleSchedule(x => x 33 // .WithIntervalInSeconds(10) 34 // .WithRepeatCount(10) 35 // .RepeatForever()) 36 // .WithDescription("This is testjob's Trigger") 37 // .Build(); 38 39 //创建时间策略 40 ITrigger trigger = TriggerBuilder.Create() 41 .WithIdentity("testtrigger1", "group1") 42 .StartAt(new DateTimeOffset(DateTime.Now.AddSeconds(10))) 43 //.StartNow()//StartAt 44 .WithCronSchedule("5/10 * * * * ?")//每隔一分钟 45 //"10,20,30,40,50,0 * * * * ?" 46 .WithDescription("This is testjob's Trigger") 47 .Build(); 48 49 trigger.JobDataMap.Add("student4", "Ray"); 50 trigger.JobDataMap.Add("student5", "心欲无痕"); 51 trigger.JobDataMap.Add("student6", "风在飘动"); 52 trigger.JobDataMap.Add("Year", DateTime.Now.Year + 1); 53 54 await scheduler.ScheduleJob(jobDetail, trigger); 55 Console.WriteLine("scheduler作业添加完成......"); 56 }
1 public class TestJob : IJob 2 { 3 public TestJob() 4 { 5 Console.WriteLine("This is TestJob的构造。。。"); 6 } 7 8 public async Task Execute(IJobExecutionContext context) 9 { 10 await Task.Run(() => 11 { 12 Console.WriteLine(); 13 Console.WriteLine("*****************************"); 14 { 15 JobDataMap dataMap = context.JobDetail.JobDataMap; 16 Console.WriteLine(dataMap.Get("student1")); 17 Console.WriteLine(dataMap.Get("student2")); 18 Console.WriteLine(dataMap.Get("student3")); 19 Console.WriteLine(dataMap.GetInt("Year")); 20 } 21 Console.WriteLine($"This is {Thread.CurrentThread.ManagedThreadId} {DateTime.Now}"); 22 //可以换成去数据库查询,可以做啥啥啥 23 //但是很多情况下,我们也是需要参数的 24 { 25 JobDataMap dataMap = context.Trigger.JobDataMap; 26 Console.WriteLine(dataMap.Get("student4")); 27 Console.WriteLine(dataMap.Get("student5")); 28 Console.WriteLine(dataMap.Get("student6")); 29 Console.WriteLine(dataMap.GetInt("Year")); 30 } 31 { 32 Console.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"); 33 JobDataMap dataMap = context.MergedJobDataMap; 34 Console.WriteLine(dataMap.Get("student1")); 35 Console.WriteLine(dataMap.Get("student2")); 36 Console.WriteLine(dataMap.Get("student3")); 37 Console.WriteLine(dataMap.Get("student4")); 38 Console.WriteLine(dataMap.Get("student5")); 39 Console.WriteLine(dataMap.Get("student6")); 40 Console.WriteLine(dataMap.GetInt("Year")); 41 } 42 Console.WriteLine("*****************************"); 43 Console.WriteLine(); 44 }); 45 } 46 }
三:任务或者定时策略传参
1:通过定时任务进行传参如下:
然后接收通过:
2:定时策略进行传参:
接收如下:
注意以上两种传参也可以统一通过下面的方式来接收参数,但是如果key相同,则会进行覆盖掉
四:常用的Trigggr
1:SimpleTrigger:从什么时间开始,间隔多久执行重复操作,可以限制最大次数,如下:
1 ITrigger trigger = TriggerBuilder.Create() 2 .WithIdentity("trigger1", "group1") 3 .StartNow() 4 .WithSimpleSchedule(x => x 5 .WithIntervalInSeconds(10) 6 .WithRepeatCount(10) 7 .RepeatForever()) //从什么时间开始,间隔多久执行重复操作,可以限制最大次数 8 .WithDescription("This is testjob's Trigger") 9 .Build();
2:Cron:表达式的方式,可以灵活订制时间规则(具体详情咨询)
1 ITrigger trigger = TriggerBuilder.Create() 2 .WithIdentity("testtrigger1", "group1") 3 .StartAt(new DateTimeOffset(DateTime.Now.AddSeconds(10))) 4 //.StartNow()//StartAt 5 .WithCronSchedule("5/10 * * * * ?")//每隔一分钟 //"10,20,30,40,50,0 * * * * ?" 6 .WithDescription("This is testjob's Trigger") 7 .Build();
五:Listener框架的各个环节--事件能做的监听
如果我们自己写定时任务的时候,因为是定时或者周期去做一些事情,所以有时候有问题或者出现了什么故障,我们只能通过我们自己写一下具体的日志,但是Quartz里面的Listener里面内置封装了一些监听接口,分别为:ISchedulerListener,ITriggerListener,IJobListener,里面有一些方法可以让我们去做一下其它的事情,我们下面分别实现了三个接口,代码如下:
1:jobListerer
1 public class CustomJobListener : IJobListener 2 { 3 public string Name => "CustomJobListener"; 4 5 public async Task JobExecutionVetoed(IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken)) 6 { 7 await Task.Run(()=> { 8 Console.WriteLine($"CustomJobListener JobExecutionVetoed {context.JobDetail.Description}"); 9 }); 10 } 11 12 public async Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken)) 13 { 14 await Task.Run(() => { 15 Console.WriteLine($"CustomJobListener JobToBeExecuted {context.JobDetail.Description}"); 16 }); 17 } 18 19 public async Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default(CancellationToken)) 20 { 21 await Task.Run(() => { 22 Console.WriteLine($"CustomJobListener JobWasExecuted {context.JobDetail.Description}"); 23 }); 24 } 25 }
2:ITriggerListener
1 public class CustomTriggerListener : ITriggerListener 2 { 3 public string Name => "CustomTriggerListener"; 4 5 public async Task TriggerComplete(ITrigger trigger, IJobExecutionContext context, SchedulerInstruction triggerInstructionCode, CancellationToken cancellationToken = default(CancellationToken)) 6 { 7 await Task.Run(() => 8 { 9 Console.WriteLine($"CustomTriggerListener TriggerComplete {trigger.Description}"); 10 }); 11 } 12 13 public async Task TriggerFired(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken)) 14 { 15 await Task.Run(() => 16 { 17 Console.WriteLine($"CustomTriggerListener TriggerFired {trigger.Description}"); 18 }); 19 } 20 21 public async Task TriggerMisfired(ITrigger trigger, CancellationToken cancellationToken = default(CancellationToken)) 22 { 23 await Task.Run(() => 24 { 25 Console.WriteLine($"CustomTriggerListener TriggerMisfired {trigger.Description}"); 26 }); 27 } 28 29 /// <summary> 30 /// 要不要放弃job 31 /// </summary> 32 /// <param name="trigger"></param> 33 /// <param name="context"></param> 34 /// <param name="cancellationToken"></param> 35 /// <returns></returns> 36 public async Task<bool> VetoJobExecution(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken)) 37 { 38 await Task.Run(() => 39 { 40 Console.WriteLine($"CustomTriggerListener TriggerMisfired {trigger.Description}"); 41 }); 42 return false;//false才能继续执行 43 } 44 }
3:ISchedulerListener
1 public class CustomSchedulerListener : ISchedulerListener 2 { 3 public async Task JobAdded(IJobDetail jobDetail, CancellationToken cancellationToken = default(CancellationToken)) 4 { 5 await Task.Run(() => 6 { 7 Console.WriteLine($"This is {nameof(CustomSchedulerListener)} JobAdded {jobDetail.Description}"); 8 }); 9 } 10 11 public Task JobDeleted(JobKey jobKey, CancellationToken cancellationToken = default(CancellationToken)) 12 { 13 throw new NotImplementedException(); 14 } 15 16 public Task JobInterrupted(JobKey jobKey, CancellationToken cancellationToken = default(CancellationToken)) 17 { 18 throw new NotImplementedException(); 19 } 20 21 public Task JobPaused(JobKey jobKey, CancellationToken cancellationToken = default(CancellationToken)) 22 { 23 throw new NotImplementedException(); 24 } 25 26 public Task JobResumed(JobKey jobKey, CancellationToken cancellationToken = default(CancellationToken)) 27 { 28 throw new NotImplementedException(); 29 } 30 31 public Task JobScheduled(ITrigger trigger, CancellationToken cancellationToken = default(CancellationToken)) 32 { 33 throw new NotImplementedException(); 34 } 35 36 public Task JobsPaused(string jobGroup, CancellationToken cancellationToken = default(CancellationToken)) 37 { 38 throw new NotImplementedException(); 39 } 40 41 public Task JobsResumed(string jobGroup, CancellationToken cancellationToken = default(CancellationToken)) 42 { 43 throw new NotImplementedException(); 44 } 45 46 public Task JobUnscheduled(TriggerKey triggerKey, CancellationToken cancellationToken = default(CancellationToken)) 47 { 48 throw new NotImplementedException(); 49 } 50 51 public Task SchedulerError(string msg, SchedulerException cause, CancellationToken cancellationToken = default(CancellationToken)) 52 { 53 throw new NotImplementedException(); 54 } 55 56 public Task SchedulerInStandbyMode(CancellationToken cancellationToken = default(CancellationToken)) 57 { 58 throw new NotImplementedException(); 59 } 60 61 public Task SchedulerShutdown(CancellationToken cancellationToken = default(CancellationToken)) 62 { 63 throw new NotImplementedException(); 64 } 65 66 public Task SchedulerShuttingdown(CancellationToken cancellationToken = default(CancellationToken)) 67 { 68 throw new NotImplementedException(); 69 } 70 71 public async Task SchedulerStarted(CancellationToken cancellationToken = default(CancellationToken)) 72 { 73 await Task.Run(() => 74 { 75 Console.WriteLine($"This is {nameof(CustomSchedulerListener)} SchedulerStarted "); 76 }); 77 } 78 79 public async Task SchedulerStarting(CancellationToken cancellationToken = default(CancellationToken)) 80 { 81 await Task.Run(() => 82 { 83 Console.WriteLine($"This is {nameof(CustomSchedulerListener)} SchedulerStarting "); 84 }); 85 } 86 87 public Task SchedulingDataCleared(CancellationToken cancellationToken = default(CancellationToken)) 88 { 89 throw new NotImplementedException(); 90 } 91 92 public Task TriggerFinalized(ITrigger trigger, CancellationToken cancellationToken = default(CancellationToken)) 93 { 94 throw new NotImplementedException(); 95 } 96 97 public Task TriggerPaused(TriggerKey triggerKey, CancellationToken cancellationToken = default(CancellationToken)) 98 { 99 throw new NotImplementedException(); 100 } 101 102 public Task TriggerResumed(TriggerKey triggerKey, CancellationToken cancellationToken = default(CancellationToken)) 103 { 104 throw new NotImplementedException(); 105 } 106 107 public Task TriggersPaused(string triggerGroup, CancellationToken cancellationToken = default(CancellationToken)) 108 { 109 throw new NotImplementedException(); 110 } 111 112 public Task TriggersResumed(string triggerGroup, CancellationToken cancellationToken = default(CancellationToken)) 113 { 114 throw new NotImplementedException(); 115 } 116 }
然后通过下面代码来融合到实例中:
1 StdSchedulerFactory factory = new StdSchedulerFactory(); 2 IScheduler scheduler = await factory.GetScheduler(); 3 scheduler.ListenerManager.AddSchedulerListener(new CustomSchedulerListener()); 4 scheduler.ListenerManager.AddTriggerListener(new CustomTriggerListener()); 5 scheduler.ListenerManager.AddJobListener(new CustomJobListener()); 6 await scheduler.Start();
这样就完美的结合在一起了,然后实例,任务,策略每次发生的动作,都会以日志的形式输出来,当然可以记录任何形式的日志。
六:LogProvider可以展示框架运行的一些信息
Quartz.Logging内置了一些记录日志的类,然后需要记录日志的话,可以直接拿来使用,不用自己再去引用配置等记录日志了,下面是自己试着写了一个:
1 public class CustomConsoleLogProvider : ILogProvider 2 { 3 public Logger GetLogger(string name) 4 { 5 return new Logger((level, func, exception, parameters) => 6 { 7 if (level >= LogLevel.Info && func != null) 8 { 9 Console.WriteLine($"[{ DateTime.Now.ToLongTimeString()}] [{ level}] { func()} {string.Join(";", parameters.Select(p => p == null ? " " : p.ToString()))} 自定义日志{name}"); 10 } 11 return true; 12 }); 13 } 14 public IDisposable OpenNestedContext(string message) 15 { 16 throw new NotImplementedException(); 17 } 18 19 public IDisposable OpenMappedContext(string key, string value) 20 { 21 throw new NotImplementedException(); 22 } 23 }
然后在DispatcherManager中的init方法中
这样即实现了log的日志
如果想要测试,可以通过:
1 static void Main(string[] args) 2 { 3 try 4 { 5 Console.WriteLine("QuartZ.Net定时调度"); 6 DispatcherManager.Init().GetAwaiter().GetResult(); 7 } 8 catch (Exception ex) 9 { 10 Console.WriteLine(ex.Message); 11 } 12 Console.Read(); 13 }