.NET中间件 -- 任务调度 - Quartz.net

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:

  1.  0 0 12 * * ?:每天12点运行
  2.  0 15 10 ? * *:每天10:15运行
  3.  0 15 10 * * ?:每天10:15运行
  4.  0 15 10 * * ? *:每天10:15运行
  5.  0 15 10 * * ? 2008:在2008年的每天10:15运行
  6.  0 * 14 * * ?:每天14点到15点之间每分钟运行一次,开始于14:00,结束于14:59。
  7.  0 0/5 14 * * ?:每天14点到15点每5分钟运行一次,开始于14:00,结束于14:55。
  8.  0 0/5 14,18 * * ?:每天14点到15点每5分钟运行一次,另每天18点到19点每5钟运行一次。
  9.  0 0-5 14 * * ?:每天14:00点到14:05,每分钟运行一次。
  10.  0 10,44 14 ? 3 WED:3月每周三的14:10分和14:44执行。
  11.  0 15 10 ? * MON-FRI:每周一,二,三,四,五的10:15分运行。
  12.  0 15 10 15 * ?:每月15日10:15分运行。
  13.  0 15 10 L * ?:每月最后一天10:15分运行。
  14.  0 15 10 ? * 6L:每月最后一个星期五10:15分运行。
  15.  0 15 10 ? * 6L 2007-2009:在2007,2008,2009年每个月的最后一个星期五的10:15分运行。
  16.  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)

[3] C# Quartz.NET实现动态改变作业调度周期 - 云+社区 - 腾讯云 (tencent.com)

[4] C# quartz.net 定时任务(二)_woflyoycm的博客-CSDN博客

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值