Quartz.NET是一个强大、开源、轻量的作业调度框架,可以用此来为执行一个作业而创建简单的或复杂的作业调度。
Nuget安装Quartz.net
流程:
创建调度器-->创建job(需要实现IJob接口)-->创建触发器(固定间隔时间 || cron表达式实现复杂的时间触发 || 使用Calendar排除Trigger中一些特定的时间节点)-->注册绑定-->添加监听器(允许我们编写监听器达到在运行时获取作业状态、处理作业数据等功能 ; 可选)-->启动
示例:
using Quartz.Impl;
using Quartz.Impl.Calendar;
using Quartz.Impl.Matchers;
using Quartz.NET_Helper.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Quartz.NET_Helper.Common
{
class QuartzManage
{
//创建调度器
private static Task<IScheduler> task_scheduler = StdSchedulerFactory.GetDefaultScheduler();
private static IScheduler scheduler = task_scheduler.Result;
public static void Interval_Execute<T>(string str) where T:IJob
{
//创建job
IJobDetail jobDetail = JobBuilder.Create<T>()
. WithIdentity("job1","group1") //job的名字和分组
.UsingJobData("param", str) //job携带的参数,键值对
.Build();
//创建简单触发器 间隔固定时间触发
ISimpleTrigger simpleTrigger = TriggerBuilder.Create()
.WithSimpleSchedule(t=>t.WithIntervalInSeconds(5) //间隔5s触发一次
.RepeatForever()) //一直执行 具体执行n次用:.WithRepeatCount(n)
.StartNow() //现在开始 今天具体的时间开始用:.StartAt(DateBuilder.TodayAt(h,m,s))
.Build() as ISimpleTrigger;
//将job和trigger注册到scheduler中
scheduler.ScheduleJob(jobDetail,simpleTrigger).Wait();
//start让调度线程启动【调度线程可以从jobstore中获取快要执行的trigger,然后获取trigger关联的job,执行job】
scheduler.Start();
}
public static void Timed_Execution<T>() where T : IJob
{
IJobDetail jobDetail= JobBuilder.Create<T>().WithIdentity("job2","group2").Build();
//使用Calendar来排除Trigger中一些特定的时间节点 19-20点不执行
DailyCalendar dailyCalendar = new DailyCalendar(DateBuilder.DateOf(19, 0, 0).DateTime.ToString("HH:MM"),DateBuilder.DateOf(20,0,0).ToString("HH:MM"));
//将Calendar注册到Scheduler中 参数:calendarname,calendar,是否替换同名clendar,是否更新trigger
scheduler.AddCalendar("myCalendar", dailyCalendar,true,true);
//使用Cron表达式创建更复杂的定时触发
ICronTrigger cronTrigger = TriggerBuilder.Create().WithIdentity("触发","1")
.WithCronSchedule(@"* 0/1 * * * ?") //Cron表达式
.ModifiedByCalendar("myCalendar") //把Calendar绑定到trigger
.Build() as ICronTrigger;
scheduler.ScheduleJob(jobDetail,cronTrigger).Wait();
//添加监听器 JobListener监听所有的job
scheduler.ListenerManager.AddJobListener(new JobListener(), GroupMatcher<JobKey>.AnyGroup());
scheduler.Start();
}
}
//实际执行的job,需要实现IJob接口,异步
public class JobTest : IJob
{
public async Task Execute(IJobExecutionContext context)
{
await Task.Run(() =>
{
//获取job携带的参数
JobDataMap datamap = context.JobDetail.JobDataMap;
string strr = datamap.GetString("param");
Console.WriteLine($"执行job1,job1携带的参数:{strr}");
//获取Trigger设置的JobDataMap
//JobDataMap triggerdatamap= context.MergedJobDataMap;
});
}
}
public class JobTest2 : IJob
{
public Task Execute(IJobExecutionContext context)
{
Console.WriteLine($"执行job2,触发时间:{context.ScheduledFireTimeUtc?.LocalDateTime} ");
return Task.FromResult(1);
}
}
public class JobListener : IJobListener
{
public string Name => nameof(JobListener);
//否决执行
public Task JobExecutionVetoed(IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.Run(()=>
{
//JobDetail的key就是job的分组和job的名字
Console.WriteLine($"job: {context.JobDetail.Key} 被否决执行");
});
}
//监听Job执行前
public Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.Run(() =>
{
Console.WriteLine($"job: {context.JobDetail.Key} 执行前");
});
}
//Job执行后
public Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.Run(() =>
{
Console.WriteLine($"job: {context.JobDetail.Key} 执行后");
});
}
}
}
运行结果:job2间隔1s执行
其他说明:
Cron表达式:由七个部分组成,依次是秒、分、时、天、月、周、年,其中年是可选的。Cron表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感。
具体说明:
- 星号(*):可用在所有字段中,表示对应时间域的每一个时刻。例如, 在分钟字段时,表示“每分钟”;
- 问号(?):该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于点位符;
- 减号(-):表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12;
- 逗号(,):表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五;
- 斜杠(/):x/y表达一个等步长序列,x为起始值,y为增量步长值。如5/15在分钟字段中表示5,20,35,50;
- L:该字符只在日期和星期字段中使用,代表“Last”的意思,但它在两个字段中意思不同。L在日期字段中,表示这个月份的最后一天,如一月的31号,非闰年二月的28号;如果L用在星期中,则表示星期六,等同于7。但是,如果L出现在星期字段里,而且在前面有一个数值X,则表示“这个月的最后X天”,例如,6L表示该月的最后星期五;
- W:该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。例如15W表示离该月15号最近的工作日,如果该月15号是星期六,则匹配14号星期五;如果15日是星期日,则匹配16号星期一;如果15号是星期二,那结果就是15号星期二。但必须注意关联的匹配日期不能够跨月,如你指定1W,如果1号是星期六,结果匹配的是3号星期一,而非上个月最后的那天。W字符串只能指定单一日期,而不能指定日期范围;
- LW组合:在日期字段可以组合使用LW,它的意思是当月的最后一个工作日;
- 井号(#):该字符只能在星期字段中使用,表示当月某个工作日。如6#3表示当月的第三个星期五(6表示星期五,#3表示当前的第三个),而4#5表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发;
eg:
- 0 0 12 * * ?:每天12点运行
- 0 15 10 ? * *:每天10:15运行
- 0 15 10 * * ?:每天10:15运行
- 0 15 10 * * ? *:每天10:15运行
- 0 15 10 * * ? 2008:在2008年的每天10:15运行
- 0 * 14 * * ?:每天14点到15点之间每分钟运行一次,开始于14:00,结束于14:59。
- 0 0/5 14 * * ?:每天14点到15点每5分钟运行一次,开始于14:00,结束于14:55。
- 0 0/5 14,18 * * ?:每天14点到15点每5分钟运行一次,另每天18点到19点每5钟运行一次。
- 0 0-5 14 * * ?:每天14:00点到14:05,每分钟运行一次。
- 0 10,44 14 ? 3 WED:3月每周三的14:10分和14:44执行。
- 0 15 10 ? * MON-FRI:每周一,二,三,四,五的10:15分运行。
- 0 15 10 15 * ?:每月15日10:15分运行。
- 0 15 10 L * ?:每月最后一天10:15分运行。
- 0 15 10 ? * 6L:每月最后一个星期五10:15分运行。
- 0 15 10 ? * 6L 2007-2009:在2007,2008,2009年每个月的最后一个星期五的10:15分运行。
- 0 15 10 ? * 6#3:每月第三个星期五的10:15分运行。
Calendar: 排除Trigger中一些特定的时间节点
流程: 创建Calendar实例 --> 将Calendar注册到scheduler容器中 --> 将Calendar绑定到触发器上
Quartz.net中一共提供了六种Calendar,六种Calendar的用法大同小异,列举如下:
【1】DailyCalendar 用于排除一天中的某一段时间
DailyCalendar calendar = new DailyCalendar(DateBuilder.DateOf(19, 0, 0).DateTime.ToString("HH:MM"), DateBuilder.DateOf(23, 0, 0).DateTime.ToString("HH:MM"));//21~23点不执行
【2】WeeklyCalendar 用于排除一周中的某几天
WeeklyCalendar calendar = new WeeklyCalendar();
calendar.SetDayExcluded(DayOfWeek.Sunday, true);//周日不执行
//注:如果想让周日恢复执行,执行代码: calendar.SetDayExcluded(DayOfWeek.Sunday, false);
【3】HolidayCalendar 用于排除某些日期
HolidayCalendar calendar = new HolidayCalendar();
calendar.AddExcludedDate(DateTime.Parse("2018/1/2")); //2018年1月2号不执行
//注:如果想让2019/1/9恢复执行,执行代码: calendar.RemoveExcludedDate(DateTime.Parse("2018/1/2"));
【4】MonthlyCalendar 用于排除每个月的某天*************************************
MonthlyCalendar calendar = new MonthlyCalendar();
calendar.SetDayExcluded(8, true); //每个月的8号不执行
//注:如果想让8号恢复执行,执行代码: calendar.SetDayExcluded(8, false);
【5】AnnualCalendar 用于排除一年中的某些天*************************************
AnnualCalendar calendar = new AnnualCalendar();
calendar.SetDayExcluded(DateTime.Parse("2018/1/2"), true);//每年1月2号不执行
//注:如果想让1月8号恢复执行,执行代码: calendar.SetDayExcluded(DateTime.Parse("2018/1/2"),true);
【6】CronCalendar 用于排除cron表达式表示的时间***************************
CronCalendar calendar = new CronCalendar("* * * 2 1 ?"); //每年的1月2号不执行
参考:
[1] Quartz.Net使用教程 - 拓荒者-NET - 博客园 (cnblogs.com)
[2] Quartz.net 3.x使用 - springsnow - 博客园 (cnblogs.com)