第九节: 利用RemoteScheduler实现Sheduler的远程控制 第八节: Quartz.Net五大构件之SimpleThreadPool及其四种配置方案 第六节: 六类Calander处理六种不同的时间场景 第五节: Quartz.Net五大构件之Trigger的四大触发类 第三节: Quartz.Net五大构件之Scheduler(创建、封装、基本方法等)和Job(创建、关联
第九节: 利用RemoteScheduler实现Sheduler的远程控制
一. RemoteScheduler远程控制
1. 背景: 在A服务器上部署了一个Scheduler,我们想在B服务器上控制这个Scheduler。
2. 猜想: A服务器上的Scheduler需要有地址、需要有端口、需要有名称(实际上也是如此)。
3. 需求: 一个控制台程序作为服务端运行A服务器上(也可以部署成服务),用另一个B服务器上的Web端控制来控制这个Scheduler的暂停、继续等操作。
4. 具体实现:
①:A服务器上的Server端的Scheduler需要配置port和bindName两个核心参数,用来对外公开。
②:B服务器上Client端的Scheduler的创建需要使用代理,并配置其地址(A服务器上对外公开的)。
(原理:通过代理获取A服务器中的Scheduler,然后获取里面的job和trigger,然后可以配置job和trigger的开启、关闭、编辑等,这里以操控job的暂停和继续为例,介绍其使用思路,详细的如何操控trigger或者编辑等,详解后面框架章节)
代码分享:
(1). A服务器上,即被控制端的Server端,这里我们用控制台程序代替。
(PS:核心点就是StdSchedulerFactory类需要配置 type、port、bindName )
1 public class RemoteSchedulerServer 2 { 3 public static void ShowRemoteScheduler() 4 { 5 //1. 配置调度器工厂 6 var schedulerFactory = new StdSchedulerFactory(new NameValueCollection() 7 { 8 {"quartz.scheduler.exporter.type","Quartz.Simpl.RemotingSchedulerExporter,Quartz" }, 9 {"quartz.scheduler.exporter.port","5555" }, 10 {"quartz.scheduler.exporter.bindName","QuartzScheduler" }, 11 }); 12 //2. 创建调度器 13 var scheduler = schedulerFactory.GetScheduler(); 14 //3. 配置job和trigger并开启 15 var job = JobBuilder.Create<HelloJob4>() 16 .WithIdentity("myJob1", "jobGroup1") 17 .Build(); 18 var trigger = TriggerBuilder.Create() 19 .WithIdentity("myJobTrigger1", "triggerGroup1") 20 .StartNow() 21 .WithCronSchedule("/1 * * ? * *") 22 .Build(); 23 scheduler.ScheduleJob(job, trigger); 24 scheduler.Start(); 25 } 26 }
(2). B服务器,即Client端,用来操控A服务器上Scheduler,下面的代码我在操控job暂停和继续的时候,直接把jobName和GroupName写死了,这里只是为了演示用法而已,实际上可以通过远程代理创建的scheduler来获取所有的job和trigger的。
(PS:核心点就是StdSchedulerFactory类需要配置 代理,并配置代理地址,即A服务器的地址、port、bindName )
1 public class RemoteSchedulerController : Controller 2 { 3 /// <summary> 4 /// 前端页面 5 /// </summary> 6 /// <returns></returns> 7 public ActionResult Index() 8 { 9 return View(); 10 } 11 /// <summary> 12 /// 使用代理的方式创建Sheduler 13 /// </summary> 14 static IScheduler scheduler = null; 15 public RemoteSchedulerController() 16 { 17 var schedulerFactory = new StdSchedulerFactory(new System.Collections.Specialized.NameValueCollection() 18 { 19 {"quartz.scheduler.proxy","true" }, //使用代理 20 {"quartz.scheduler.proxy.Address","tcp://localhost:5555/QuartzScheduler" } //Server端的地址是多少,localhost就是多少 21 }); 22 scheduler = schedulerFactory.GetScheduler(); 23 scheduler.Start(); 24 } 25 /// <summary> 26 /// 暂停Job 27 /// (这里直接从前端默认把名称传过来,实际可以从scheduler中拿到) 28 /// </summary> 29 /// <returns></returns> 30 public ActionResult PauseJob(string jobName, string groupName) 31 { 32 try 33 { 34 scheduler.PauseJob(new JobKey(jobName, groupName)); 35 return Content("ok"); 36 } 37 catch (Exception) 38 { 39 40 return Content("error"); 41 } 42 43 } 44 /// <summary> 45 /// 恢复Job 46 /// </summary> 47 /// <returns></returns> 48 public ActionResult ResumeJob(string jobName, string groupName) 49 { 50 try 51 { 52 scheduler.ResumeJob(new JobKey(jobName, groupName)); 53 return Content("ok"); 54 } 55 catch (Exception) 56 { 57 58 return Content("error"); 59 } 60 } 61 }
1 <html> 2 <head> 3 <meta name="viewport" content="width=device-width" /> 4 <title>Index</title> 5 <script src="~/Scripts/jquery-1.10.2.min.js"></script> 6 <script> 7 $(function () { 8 //1. 暂停 9 $("#btn1").on("click", function () { 10 $.post("PauseJob", { "jobName": "myJob1", "groupName": "jobGroup1" }, function (data) { 11 if (data=="ok") { 12 alert("暂停成功"); 13 } else { 14 alert("失败了"); 15 } 16 }); 17 }); 18 //2. 继续 19 $("#btn2").on("click", function () { 20 $.post("ResumeJob", { "jobName": "myJob1", "groupName": "jobGroup1" }, function (data) { 21 if (data == "ok") { 22 alert("继续成功"); 23 } else { 24 alert("失败了"); 25 } 26 }); 27 }); 28 }); 29 </script> 30 </head> 31 <body> 32 <div> 33 远程操控另一个服务器端的Sheduler 34 </div> 35 <p></p><p></p><p></p> 36 <button id="btn1">暂停</button> 37 <button id="btn2">继续</button> 38 </body> 39 </html>
(3). 运行结果
第八节: Quartz.Net五大构件之SimpleThreadPool及其四种配置方案
一. 简介
揭秘: SimpleThreadPool是Quartz.Net中自带的线程池,默认个数为10个,代表一个Scheduler同一时刻并发的最多只能执行10个job,超过10个的job需要排队等待。
二. 四种配置方案
1. NameValueCollection的方式
需要利用StdSchedulerFactory的构造函数进行传进去,向哪个Sheduler中传,即配置哪个Sheduler的对应的线程池。
代码分享:
1 { 2 var pairs = new System.Collections.Specialized.NameValueCollection() { }; 3 pairs.Add("quartz.threadPool.ThreadCount", "20"); //设置线程池个数为20 4 5 var factory = new StdSchedulerFactory(pairs); //将前面的配置加到Scheduler工厂中 6 var scheduler = factory.GetScheduler(); 7 scheduler.Start(); 8 9 var meta = scheduler.GetMetaData(); 10 int threadPoolSize = meta.ThreadPoolSize; 11 Console.WriteLine("线程池的个数为:{0}", threadPoolSize); 12 }
2. App.config的方式配置,
详见:App.config文件,该模式代码中不需要进行任何的额外配置,适用于所有的Sheduler。
配置文件代码分享:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <!--线程池个数设置 开始--> 4 5 <configSections> 6 <section name="quartz" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089"/> 7 </configSections> 8 <quartz> 9 <!--设置Sheduler的线程池个数为22--> 10 <add key="quartz.threadPool.threadCount" value="22"/> 11 </quartz> 12 13 <!--线程池个数设置 结束--> 14 <startup> 15 <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/> 16 </startup> 17 </configuration>
3. quartz.config文件的形式进行配置
该模式代码中不需要进行任何的额外配置,适用于所有的Sheduler。
用法:新建名为"quartz.config"的xml文件,在文件中写入:quartz.threadPool.threadCount=15 代表线程池个数设置为15, 同时要把该文件的属性设置为始终复制,使其可以生产到bin文件。
分享一下quartz.config中的代码
PS:就一句话哦。
1 quartz.threadPool.threadCount=15
4. 通过代码设置电脑的环境变量来实现
一句代码:Environment.SetEnvironmentVariable("quartz.threadPool.threadCount", "26"); 设置后,适用于所有的Sheduler。
代码分享:
{ //将线程池的个数设置为26 Environment.SetEnvironmentVariable("quartz.threadPool.threadCount", "26"); var factory = new StdSchedulerFactory(); var scheduler = factory.GetScheduler(); scheduler.Start(); var meta = scheduler.GetMetaData(); int threadPoolSize = meta.ThreadPoolSize; Console.WriteLine("线程池的个数为:{0}", threadPoolSize); }
总结:以上4种方式的优先级为:quartz.config < app.config < 环境变量 < namevaluecollection
第六节: 六类Calander处理六种不同的时间场景
背景介绍及其使用
该章节主要补充介绍,在前一章四类触发器的基础上配合六大Canlander来动态删减某些时间,来满足更多的应用场景。
1. DailyCalendar:动态排除某天的某些字段.
(需求:每天8-23点执行,每隔1s执行一次,但是21-22点这个区间不执行)
2. WeeklyCalendar:适合在星期几的维度上做“减法操作”
(需求:每天8-23点执行,每隔1s执行一次,但是周五这一天不执行)
3. HolidayCalendar:适合当年的某一天不能执行
(需求:每天8-23点执行,每隔1s执行一次,但是今年的6月16号这一天不执行)
4. MonthlyCalendar:适合某个月中的某一天不能执行
(需求:每天8-23点执行,每隔1s执行一次,但是每月的27号不执行)
5. AnnualCalendar:适合每年指定的某一天不能执行(有问题)
(需求:每天8-23点执行,每隔1s执行一次,但是每年的6月16号这一天不执行)
6. CronCalendar:字符串表达式来排除某一天,某一个月份,某一年都可以
(需求:每天8-23点执行,每隔1s执行一次,但是2月27号这天不执行)
代码分享:
1 public static void CalanderShow() 2 { 3 //1. 每天8-23点执行,每隔1s执行一次,但是21-22点这个区间不执行 4 { 5 IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler(); 6 scheduler.Start(); 7 //单独记录一个区间段 21-22点 8 DailyCalendar dailyCalendar = new DailyCalendar(DateBuilder.DateOf(21, 0, 0).DateTime, 9 DateBuilder.DateOf(22, 0, 0).DateTime); 10 scheduler.AddCalendar("mycalendar", dailyCalendar, true, true); 11 12 var job = JobBuilder.Create<HelloJob>().Build(); 13 var trigger = TriggerBuilder.Create().WithDailyTimeIntervalSchedule( 14 x => x.OnEveryDay() 15 .StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(8, 00)) 16 .EndingDailyAt(TimeOfDay.HourAndMinuteOfDay(23, 00)) 17 .WithIntervalInSeconds(1) 18 ) 19 .ModifiedByCalendar("mycalendar") 20 .Build(); 21 scheduler.ScheduleJob(job, trigger); 22 } 23 24 //2. 每天8-23点执行,每隔1s执行一次,但是周五这一天不执行 25 { 26 IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler(); 27 scheduler.Start(); 28 //设定周五不能执行 29 WeeklyCalendar calendar = new WeeklyCalendar(); 30 calendar.SetDayExcluded(DayOfWeek.Friday, true); 31 scheduler.AddCalendar("mycalendar", calendar, true, true); 32 33 var job = JobBuilder.Create<HelloJob>().Build(); 34 var trigger = TriggerBuilder.Create().WithDailyTimeIntervalSchedule( 35 x => x.OnEveryDay() 36 .StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(8, 00)) 37 .EndingDailyAt(TimeOfDay.HourAndMinuteOfDay(23, 00)) 38 .WithIntervalInSeconds(1) 39 ) 40 .ModifiedByCalendar("mycalendar") 41 .Build(); 42 scheduler.ScheduleJob(job, trigger); 43 } 44 45 //3. 每天8-23点执行,每隔1s执行一次,但是当年6月16号这一天不执行 46 { 47 IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler(); 48 scheduler.Start(); 49 HolidayCalendar calendar = new HolidayCalendar(); 50 calendar.AddExcludedDate(DateTime.Parse("06-16")); //把当年6月16日排除在外 51 52 scheduler.AddCalendar("mycalendar", calendar, true, true); 53 var job = JobBuilder.Create<HelloJob>().Build(); 54 var trigger = TriggerBuilder.Create().WithDailyTimeIntervalSchedule( 55 x => x.OnEveryDay() 56 .StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(8, 00)) 57 .EndingDailyAt(TimeOfDay.HourAndMinuteOfDay(23, 00)) 58 .WithIntervalInSeconds(1) 59 ) 60 .ModifiedByCalendar("mycalendar") 61 .Build(); 62 scheduler.ScheduleJob(job, trigger); 63 64 } 65 66 //4. 每天8-23点执行,每隔1s执行一次,但是每月的27号不执行 67 { 68 IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler(); 69 scheduler.Start(); 70 71 //指定月份中的某一天不能执行 72 MonthlyCalendar calendar = new MonthlyCalendar(); 73 calendar.SetDayExcluded(27, true); //将27号这天排除在外 74 scheduler.AddCalendar("mycalendar", calendar, true, true); 75 76 var job = JobBuilder.Create<HelloJob>().Build(); 77 var trigger = TriggerBuilder.Create().WithDailyTimeIntervalSchedule( 78 x => x.OnEveryDay() 79 .StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(8, 00)) 80 .EndingDailyAt(TimeOfDay.HourAndMinuteOfDay(23, 00)) 81 .WithIntervalInSeconds(1) 82 ) 83 .ModifiedByCalendar("mycalendar") 84 .Build(); 85 86 scheduler.ScheduleJob(job, trigger); 87 } 88 89 //5. 每天8-23点执行,每隔1s执行一次,但是每年的6月16号这一天不执行 90 { 91 IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler(); 92 scheduler.Start(); 93 94 AnnualCalendar calendar = new AnnualCalendar(); 95 calendar.SetDayExcluded(DateTime.Parse("06-16"), true); //把每年的6月16日排除在外 96 scheduler.AddCalendar("mycalendar", calendar, true, true); 97 98 var job = JobBuilder.Create<HelloJob>().Build(); 99 var trigger = TriggerBuilder.Create().WithDailyTimeIntervalSchedule( 100 x => x.OnEveryDay() 101 .StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(8, 00)) 102 .EndingDailyAt(TimeOfDay.HourAndMinuteOfDay(23, 00)) 103 .WithIntervalInSeconds(1) 104 ) 105 .ModifiedByCalendar("mycalendar") 106 .Build(); 107 scheduler.ScheduleJob(job, trigger); 108 } 109 110 //6.每天8-23点执行,每隔1s执行一次,但是2月27号这天不执行 111 { 112 IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler(); 113 scheduler.Start(); 114 115 CronCalendar calendar = new CronCalendar("* * * 27 2 ?"); 116 scheduler.AddCalendar("mycalendar", calendar, true, true); 117 118 var job = JobBuilder.Create<HelloJob>().Build(); 119 var trigger = TriggerBuilder.Create().WithDailyTimeIntervalSchedule( 120 x => x.OnEveryDay() 121 .StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(8, 00)) 122 .EndingDailyAt(TimeOfDay.HourAndMinuteOfDay(23, 00)) 123 .WithIntervalInSeconds(1) 124 ) 125 .ModifiedByCalendar("mycalendar") 126 .Build(); 127 scheduler.ScheduleJob(job, trigger); 128 } 129 130 }
第五节: Quartz.Net五大构件之Trigger的四大触发类
一. WithSimpleSchedule(ISimpleTrigger)
1. 用途:时、分、秒上的轮询(和timer类似),实际开发中,该场景占绝大多数.
2. 轮询的种类:永远轮询和限定次数轮询.
3. 参数中的几个函数:
A.执行间隔:
①.WithInterval(TimeSpan timeSpan):通用的间隔执行方法
②.WithIntervalInHours(int hours):以小时为间隔单位进行执行
③.WithIntervalInMinutes(int minutes):以分钟为间隔单位进行执行
④.WithIntervalInSeconds(int seconds):以秒为间隔单位进行执行
B.执行时间:
①.WithRepeatCount(int repeatCount):执行多少次以后结束
②.RepeatForever():永远执行
③.repeatMinutelyForever():一分钟执行一次(永远执行)
repeatMinutelyForever(int minutes):每隔几分钟执行一次(永远执行)
repeatMinutelyForTotalCount(int count, int minutes):每隔几分钟执行一次(执行次数为count)
类似的还有秒、小时。
代码分享:
1 public static void SimpleTriggrShow() 2 { 3 //1. 创建Schedule 4 IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler(); 5 //2. 创建Job 6 var job1 = JobBuilder.Create<HelloJob>().Build(); 7 //3. 创建Trigger 8 //1s执行一次,永远执行 9 var trigger = TriggerBuilder.Create() 10 .WithSimpleSchedule(x => x.WithIntervalInSeconds(1).RepeatForever()) 11 .Build(); 12 //2s执行一次,执行10次 13 //var trigger = TriggerBuilder.Create() 14 // .WithSimpleSchedule(x => x.WithIntervalInSeconds(2).WithRepeatCount(10)) 15 // .Build(); 16 //注意这种用法:WithScheduler,表示1s执行一次,执行了5次 17 //var trigger = TriggerBuilder.Create() 18 // .WithSchedule(SimpleScheduleBuilder.RepeatSecondlyForTotalCount(5, 1)) 19 // .Build(); 20 //4. 开始调度 21 scheduler.ScheduleJob(job1, trigger); 22 scheduler.Start(); 23 }
二. WithCalendarIntervalSchedule (ICalendarTrigger)
1.用途:与日历相关
2.参数中的几个函数:
①.WithInterval(TimeSpan timeSpan):通用的间隔执行方法
②.WithIntervalInHours(int hours):以小时为间隔单位进行执行
③.WithIntervalInMinutes(int minutes):以分钟为间隔单位进行执行
④.WithIntervalInSeconds(int seconds):以秒为间隔单位进行执行
⑤.WithIntervalInDays(int days):以天为间隔单位进行执行
⑥.WithIntervalInMonths(int months):以月为间隔单位进行执行
代码分享:
1 public static void CalendarIntervalTriggerShow() 2 { 3 //1. 创建Schedule 4 IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler(); 5 scheduler.Start(); 6 //2. 创建Job 7 var job1 = JobBuilder.Create<HelloJob>().Build(); 8 //3. 创建Trigger 9 //3s执行一次,60s后结束 10 var trigger = TriggerBuilder.Create() 11 .WithCalendarIntervalSchedule(x => x.WithIntervalInSeconds(3)) 12 .EndAt(DateTimeOffset.Now.AddSeconds(60)) //60s后结束 13 .Build(); 14 //4. 开始调度 15 scheduler.ScheduleJob(job1, trigger); 16 }
三. WithDailyTimeIntervalSchedule (IDailyTimeTrigger)
1. 用途:解决时间点的增、减、排除。
2. 核心函数:
a. OnEveryDay:每天
b. OnMondayThroughFriday:周一至周五,即工作日
c. OnSaturdayAndSunday:周六至周天,即休息日
d. OnDaysOfTheWeek:用数组的形式单独来指定一周中的哪几天
e. StartingDailyAt:表示开始于几点 (区别于前面的StartAt)
f. EndingDailyAt:表示结束于几点 (区别于前面的EndAt)
代码分享:
1 public static void DailyTimeIntervalTriggerShow() 2 { 3 //1. 创建Schedule 4 IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler(); 5 //2. 创建Job 6 var job1 = JobBuilder.Create<HelloJob>().Build(); 7 //3. 创建Trigger 8 //每天8-20点,每半个小时执行一次(即8:00、8:30 。。。。 19:30、20:30) 9 var trigger1 = TriggerBuilder.Create().WithDailyTimeIntervalSchedule( 10 x => x.OnEveryDay() 11 .StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(8, 00)) 12 .EndingDailyAt(TimeOfDay.HourAndMinuteOfDay(20, 00)) 13 .WithIntervalInMinutes(30)) 14 .Build(); 15 16 //每个工作日的凌晨2点执行1次 (这里的设计是2点开始,2:01结束,每个一小时执行一次,说白了总共执行了一次) 17 //或者直接WithIntervalInHours替换成WithRepeatCount 18 var trigger2 = TriggerBuilder.Create().WithDailyTimeIntervalSchedule( 19 x => x.OnMondayThroughFriday() 20 .StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(2, 00)) 21 .EndingDailyAt(TimeOfDay.HourAndMinuteOfDay(2, 01)) 22 .WithIntervalInHours(1)) 23 .Build(); 24 25 //每个周的周一和周四的2点执行1次 (这里的设计是2点开始,2:01结束,每个一小时执行一次,说白了总共执行了一次) 26 //或者直接WithIntervalInHours替换成WithRepeatCount 27 var trigger3 = TriggerBuilder.Create().WithDailyTimeIntervalSchedule( 28 x => x.OnDaysOfTheWeek(new DayOfWeek[2] { 29 DayOfWeek.Monday, DayOfWeek.Thursday }) 30 .StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(2, 00)) 31 .EndingDailyAt(TimeOfDay.HourAndMinuteOfDay(2, 01)) 32 .WithIntervalInHours(1)) 33 .Build(); 34 //4. 开始调度 35 scheduler.ScheduleJob(job1, trigger1); 36 scheduler.Start(); 37 }
四. WithCronSchedule (ICronTrigger)
1. 用途:使用cron表达式代替硬编码,可以替代以上三种Trigger (详见:www.cnblogs.com/knowledgesea/p/4705796.html)
2. 规则:
a 整体规则排列如下,且日和周必须有一个位数是 ?
* * * * * *
秒 分 时 日 月 周
b ?: 代表示模糊的意思,必须存在,且只能在日或周中的一个存在
c *: 最小单位轮询,在分钟的字段域里,表示每分钟;在小时的字段域里,表示每小时
d /: 表示递增: 如0/5在秒的字段域里,表示第0、5、15、20.... 秒 可以省略0,即 /5
e -: 表示范围, 如1-10在秒字段域里,表示1s、2s、3s到10s都执行
f ,: 表示并且, 如1,10,20在秒字段域里,表示1s,10s,20s都执行
g #: 只能存在周这一个域,表示第几周的星期几,如果超出范围,则忽略不记,如2#4,表示第四周的星期二
h L: 表示last的意思: 天: 10L 表示本月的倒数第十天执行, 5L 表示本月的最后一个周四执行(暂不研究)
3. 补充一下秒、分、时、日、月、周的字段域范围
秒: 0-59
分: 0-59
时: 0-23
日: 1-31
月: 1-12 或 JAN-DEC
周: 1-7 或 SUN-SAT
年:选填,可以留空, 1970-2099
4. 补充几个事例帮助理解:
实例1:0**1*? note:每月1号凌晨都会被执行。
实例2:0**?** note:每分钟的00秒被执行。
实例3:0 10 18 ? 3 WEB note:每年3月的每个星期三,下午6点10分都会被触发
实例4:0 10 18 15 3 ? note:每年三月的第15天,下午6点10分都会被触发
实例5:0 10 18 1-5 * ? note:每月的1号到5号(包含每月1号和5号,每月共计5天都会被触发),下午6点10分都会被触发
实例6:0 10-15 * ? * * note:每小时的第10分钟到第15分钟(包含每小时的第10分钟和第15分钟,每小时共计5分钟都会被触发),都会被触发
实例7:10,20 * * ? * * note:每分钟的第10秒与第20秒都会被触发
实例8:0 10,20 * 1,2 * ? note:每月的第1天与第2天的,每小时的第10分钟与第20分钟被触发。
实例9:5/20 * * ? * * note:每分钟的第5秒,第25秒,第45秒 都会被执行。
实例10:0 * 2/2 ? * * note:每天的第2小时,第4小时,第6小时,第8小时 ... 第22小时的00分00秒都会被触发。
实例11:* * * ? * 3#4 note:每月的第4个星期的周2,凌晨触发。
实例12:* * * ? * 6#2 note:每月的第2个星期的周5,凌晨触发
代码分享:
1 public static void CronTriggerShow() 2 { 3 //1. 创建Schedule 4 IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler(); 5 scheduler.Start(); 6 7 //2. 创建Job 8 var job1 = JobBuilder.Create<HelloJob>().Build(); 9 10 //3. 创建Trigger 11 12 //每天8-20点,每半个小时执行一次(即8:00、8:30 。。。。 19:30、20:30) 13 var trigger1 = TriggerBuilder.Create().WithCronSchedule("0 0/30 8-20 * * ?") 14 .Build(); 15 //每个工作日的凌晨2点执行1次 16 var trigger2 = TriggerBuilder.Create().WithCronSchedule("0 0 2 ? * Mon-Fri") 17 .Build(); 18 19 //每个周的周一和周四的2点执行1次 20 var trigger3 = TriggerBuilder.Create().WithCronSchedule("0 0 2 ? * Mon,Wes") 21 .Build(); 22 23 24 //4. 开始调度 25 scheduler.ScheduleJob(job1, trigger2); 26 }
第三节: Quartz.Net五大构件之Scheduler(创建、封装、基本方法等)和Job(创建、关联等)
一. 五大构件
引言: Quartz.Net的五大构件
1. 调度器:Scheduler
2. 作业任务:Job
3. 触发器: Trigger
4. 线程池: SimpleThreadPool
5. 作业持久化:JobStore
二. Scheduler详解
1. 创建Scheduler的两种方式
(1). 直接通过StdSchedulerFactory类的GetDefaultScheduler方法创建
(2). 先创建StdSchedulerFactory,然后通过GetScheduler方法创建. 该方式可以在实体化StdSchedulerFactory的时候配置一些额外的信息,比如:配置SimpleThreadPool的个数、RemoteScheduler的远程控制、数据库的持久化等。(都在后续章节介绍)
2. Scheduler的简单封装
这里提供两种思路,一种是单例的模式封装,另一种是利用线程槽的模式封装
(1). 单例模式:是指无论多少个用户访问,都只有一个实例,在web端上常用 (详见:MySchedulerFactory类)
(2). 线程槽模式:是指单个用户的单次链接,在未断开连接之前,只有一个实例,下次重新连接,实例将重新创建(详见:MySchedulerFactory2类)
代码分享:
1 /// <summary> 2 /// 将Sheduler封装成单例模式,解决多线程多用户不唯一的问题 3 /// </summary> 4 public class MySchedulerFactory 5 { 6 /// <summary> 7 /// 静态变量:由CLR保证,在程序第一次使用该类之前被调用,而且只调用一次 8 /// </summary> 9 private static IScheduler _Scheduler = StdSchedulerFactory.GetDefaultScheduler(); 10 public static IScheduler CreateScheduler() 11 { 12 return _Scheduler; 13 } 14 } 15 /// <summary> 16 /// 通过线程槽进行一个优化 17 /// </summary> 18 public class MySchedulerFactory2 19 { 20 public static IScheduler CreateScheduler() 21 { 22 IScheduler scheduler = CallContext.GetData(typeof(MySchedulerFactory2).Name) as IScheduler; 23 if (scheduler == null) 24 { 25 scheduler = StdSchedulerFactory.GetDefaultScheduler(); 26 CallContext.SetData(typeof(MySchedulerFactory2).Name, scheduler); 27 } 28 return scheduler; 29 } 30 }
3. Scheduler的基本方法:
(1). 开启:Start
(2). 关闭:ShutDown
(3). 暂停job或Trigger:PauseAll、PauseJob、PauseJobs、PauseTrigger、PauseTriggers
(4). 恢复job或Trigger:ResumeAll、ResumeJob、ResumeJobs、ResumeTrigger、ResumeTriggers
(5). 将job和trigger加入Scheduler中:ScheduleJob
(6). 添加Job:AddJob
PS:更多方法以及如何封装使用,将在后面的框架章节介绍
分享一段完成的代码:
1 public static void SchedulerShow() 2 { 3 //1.创建Scheduler有两种方式 4 //方式一:直接通过StdSchedulerFactory类的GetDefaultScheduler方法创建 5 IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler(); 6 //方式二:先创建StdSchedulerFactory,然后通过GetScheduler方法创建 7 var factory = new StdSchedulerFactory(); 8 IScheduler scheduler2 = factory.GetScheduler(); 9 10 //2.创建一个具体的作业即job (具体的job需要单独在一个文件中执行) 11 var job = JobBuilder.Create<HelloJob>().Build(); 12 13 //3.创建并配置一个触发器即trigger 1s执行一次 14 var trigger = TriggerBuilder.Create().WithSimpleSchedule(x => x.WithIntervalInSeconds(1) 15 .RepeatForever()).Build(); 16 //4.将job和trigger加入到作业调度池中 17 scheduler.ScheduleJob(job, trigger); 18 19 //5.开启调度 20 scheduler.Start(); 21 }
三. Job详解
1. 几个类型
①. JobBuilder:用来创建JobDetail。
②. IJob:具体作业任务需要实现该接口,并实现里面的方法
③. IJobDetail:用来定义工作实例
2. job的创建有两种形式:
①.Create的泛型方式:写起来代码简洁方便。
②.反射+OfType的方式:用于后期动态绑定,通过程序集的反射
1 //1 (Create的泛型方式) 2 IJobDetail job1 = JobBuilder.Create<HelloJob2>() 3 .UsingJobData("name", "ypf") 4 .UsingJobData("age", "12") 5 .WithIdentity("job1", "myJob1") 6 .WithDescription("我是用来对该job进行描述的") 7 .StoreDurably(true) 8 .Build(); 9 10 //2 (反射+OfType的方式) 11 //通过反射来创建类 12 var type = Assembly.Load("QuartzDemo").CreateInstance("QuartzDemo.HelloJob2"); 13 //OfType的方式加载类型 14 IJobDetail job2 = JobBuilder.Create().OfType(type.GetType()) 15 .UsingJobData("name", "ypf") 16 .UsingJobData("age", "12") 17 .StoreDurably(true) 18 .Build();
3.常用的几个方法
①.UsingJobData:给Job添加一些附加值,存储在JobDataMap里,可以在具体的Job中获取。(通过context.JobDetail.JobDataMap获取)
②.StoreDurably:让该job持久化,不被销毁.(默认情况下为false,即job没有对应的trigger的话,job就被销毁)
③.WithIdentity:身份标记,给job起个名称,便于和Trigger关联的时候使用.
④.WithDescription:用来对job进行描述,并没有什么实际作用
分享完整代码:
/// <summary> /// Job详解 /// </summary> public static void JobShow() { //1. 创建Schedule IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler(); //2. 创建Job //2.1 (Create的泛型方式) IJobDetail job1 = JobBuilder.Create<HelloJob2>() .UsingJobData("name", "ypf") .UsingJobData("age", "12") .WithIdentity("job1", "myJob1") .WithDescription("我是用来对该job进行描述的") .StoreDurably(true) .Build(); //2.2 (反射+OfType的方式) //通过反射来创建类 var type = Assembly.Load("QuartzDemo").CreateInstance("QuartzDemo.HelloJob2"); //OfType的方式加载类型 IJobDetail job2 = JobBuilder.Create().OfType(type.GetType()) .UsingJobData("name", "ypf") .UsingJobData("age", "12") .StoreDurably(true) .Build(); IJobDetail job3 = JobBuilder.Create(type.GetType()) .UsingJobData("name", "ypf") .UsingJobData("age", "12") .StoreDurably(true) .Build(); //3. 创建Trigger ITrigger trigger = TriggerBuilder.Create().WithSimpleSchedule(x => x.WithIntervalInSeconds(1).RepeatForever()).Build(); //4. 将Job和Trigger加入调度器中 //scheduler.ScheduleJob(job1, trigger); //scheduler.ScheduleJob(job2, trigger); scheduler.ScheduleJob(job3, trigger); //5. 开始调度 scheduler.Start(); } /// <summary> /// 实现IJob接口 /// </summary> class HelloJob2 : IJob { void IJob.Execute(IJobExecutionContext context) { var name = context.JobDetail.JobDataMap["name"]; var age = context.JobDetail.JobDataMap["age"]; Console.WriteLine("name值为:{0},age值为:{1}", name, age); } }
运行结果:
4. Job和触发器关联的形式:1对1、1对多、多对1
(PS:在下面Trigger处详细介绍)
第二节:比较DateTime和DateTimeOffset两种时间类型并介绍Quartz.Net中用到的几类时间形式(定点、四舍五入、倍数、递增)
一. 时间的类型
1. 背景
这里为什么要介绍时间类型呢,明明是定时调度篇,原因是在定时任务中,任务什么时间开始执行,什么时间结束执行,要用到各种各样的时间模式,虽然这不能算是一个复杂的问题,但在正式介绍Quartz.Net之前,还是很有必要补充一下的,该章节就是解决这类问题的。
2. 时间类型
时间类型主要有两类:DateTime和DateTimeOffset
(详情参考:https://docs.microsoft.com/zh-cn/dotnet/standard/datetime/choosing-between-datetime)
(1). DateTime:表示的时区有限,国内采用这个时间。
(2). DateTimeOffset:可以表示任何时区,通过偏移量来控制。(Quartz中提供DateBuilder类来实现DateTimeOffset类型)
3. 类型转换
(1). DateTime→DateTimeOffset 利用DateTimeOffset的构造函数
(2). DateTimeOffset→DateTime 利用Convert.ToDateTime方法
下面分享一段两种类型相互转换的代码:
1 // DateTime类型 2 DateTime date1 = DateTime.Parse("2018-01-01 11:45:30"); 3 //DateTimeOffset类型 4 DateTimeOffset date3 = DateBuilder.DateOf(11, 45, 30, 1, 1, 2018); 5 //1. DateTime 转换成 DateTimeOffset 6 DateTimeOffset date16 = new DateTimeOffset(date1, TimeSpan.Zero); 7 //2. DateTimeOffset 转换成 DateTime 8 DateTime date17 = Convert.ToDateTime(date3);
二. 各种模式的表示
(一). 定点模式
1. 需求:
a. 2018-01-01 11:45:30
b. 01-01 1:45:30
c. 1:45:30
2. 解决方案
(1). DateTime类
a. 利用DateTime.Parse()进行转换,如:DateTime.Parse("2018-01-01 11:45:30");
b. 利用DateTime类丰富的构造函数来执行, 如:new DateTime(2018, 1, 1, 11, 45, 30);
(2). DateTimeOffset类
a. 利用DateBuilder.DateOf()进行转换,如:DateBuilder.DateOf(11, 45, 30, 1, 1, 2018);
b. 利用DateTimeOffset类丰富的构造函数来执行, 如: new DateTimeOffset(2018, 1, 1, 11, 45, 30, TimeSpan.Zero);
c. 另外,DateBuilder类还提供 TodayAt和TomorrowAt类,便于在当前年月日的基础上进行处理
代码分享:
1 DateTime date1 = DateTime.Parse("2018-01-01 11:45:30"); 2 DateTime date2 = new DateTime(2018, 1, 1, 11, 45, 30); 3 DateTimeOffset date3 = DateBuilder.DateOf(11, 45, 30, 1, 1, 2018); 4 DateTimeOffset date4 = new DateTimeOffset(2018, 1, 1, 11, 45, 30, TimeSpan.Zero); 5 //默认为当前年月日 6 DateTimeOffset date5 = DateBuilder.TodayAt(1, 45, 30); 7 //默认为当前年月日的基础上 + 1天 8 DateTimeOffset date6 = DateBuilder.TomorrowAt(1, 45, 30);
(二). 四舍五入的模式
1. 需求:1:45:30 → 2:00:00
→ 1:00:00
2. 解决方案:
(1).DateBuilder.EvenHourDate 在小时的基础上进行“入”
(2).DateBuilder.EvenHourDateBefore 在小时的基础上进行“舍”
(3).另外在分钟的基础上进行入和舍有: EvenMinuteDate和EvenMinuteDateBefore
在秒钟的基础上进行入和舍有: EvenSecondDate和EvenSecondDateBefore
代码分享:
1 DateTimeOffset date5 = DateBuilder.TodayAt(1, 45, 30); 2 DateTimeOffset date7 = DateBuilder.EvenHourDate(date5); //当前年月日下:2:00:00 3 DateTimeOffset date8 = DateBuilder.EvenHourDateBefore(date5); //当前年月日下:1:00:00
(三). 倍数模式(不常用)
1. 包括:NextGivenMinuteDate和NextGivenSecondDate
以NextGivenMinuteDate为例,说明它的用法,NextGivenSecondDate与他类似
查看源码:public static DateTimeOffset NextGivenMinuteDate(DateTimeOffset? date, int minuteBase);
第一个参数:可以为空,也可以指定时间
第二个参数:把一个小时按minuteBase分钟进行划分,也就是60/minuteBase等份,真正的运行时间所在区间的下一个minuteBase分钟运行,
(PS:比如minuteBase=20,那么就是将分钟划分为3等分,分别是:20、40、60, 比如现在分钟是在 0-19分59秒,任何一个都会变为 20分00秒)
2. 用法:第一个参数为空的话,取的是当前时间为依据。
第一个参数有值的话,是以第一个参数为依据。
代码分享:
//以当前时间为依据,假设当前时间为:14:43:29 d9=14:50:00 d10=15:00:00
DateTimeOffset d9 = DateBuilder.NextGivenMinuteDate(null, 10); DateTimeOffset d10 = DateBuilder.NextGivenMinuteDate(null, 20); //以第一个参数为依据 DateTimeOffset date9 = DateBuilder.NextGivenMinuteDate(DateBuilder.TodayAt(1, 45, 30), 10); //50分 秒数为0 DateTimeOffset date10 = DateBuilder.NextGivenMinuteDate(new DateTime(2018, 1, 1, 11, 25, 30), 20); //40分 秒数为0
(四). 递增模式
1. 需求:解决在某个时间点上增加:秒、分、分钟、小时、或天、月等。
2. 解决方案:
利用DateTime类中的各种Add函数来解决。
常用方法有:AddSeconds、AddMinutes、AddHours、AddDays、AddMonths
代码分享:
1 DateTime date1 = DateTime.Parse("2018-01-01 11:45:30"); 2 DateTime date11 = date1.AddSeconds(1); //2018-01-01 11:45:31 3 DateTime date12 = date1.AddMinutes(1); //2018-01-01 11:46:30 4 DateTime date13 = date1.AddHours(1); //2018-01-01 12:45:30 5 DateTime date14 = date1.AddDays(1); //2018-01-02 11:45:30 6 DateTime date15 = date1.AddMonths(1); //2018-02-01 11:45:30