第九节: 利用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


浙公网安备 33010602011771号