第九节: 利用RemoteScheduler实现Sheduler的远程控制 第八节: Quartz.Net五大构件之SimpleThreadPool及其四种配置方案 第六节: 六类Calander处理六种不同的时间场景 第五节: Quartz.Net五大构件之Trigger的四大触发类 第三节: Quartz.Net五大构件之Scheduler(创建、封装、基本方法等)和Job(创建、关联

2023-01-02,,,,

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

第九节: 利用RemoteScheduler实现Sheduler的远程控制 第八节: Quartz.Net五大构件之SimpleThreadPool及其四种配置方案 第六节: 六类Calander处理六种不同的时间场景 第五节: Quartz.Net五大构件之Trigger的四大触发类 第三节: Quartz.Net五大构件之Scheduler(创建、封装、基本方法等)和Job(创建、关联的相关教程结束。

《第九节: 利用RemoteScheduler实现Sheduler的远程控制 第八节: Quartz.Net五大构件之SimpleThreadPool及其四种配置方案 第六节: 六类Calander处理六种不同的时间场景 第五节: Quartz.Net五大构件之Trigger的四大触发类 第三节: Quartz.Net五大构件之Scheduler(创建、封装、基本方法等)和Job(创建、关联.doc》

下载本文的Word格式文档,以方便收藏与打印。