如何通过配置为Quartz任务添加日历以屏蔽节假日执行?
问题背景
我希望部分Quartz任务在节假日停止执行,Quartz官方文档提到:
实现ICalendar接口的Quartz.NET Calendar对象可在触发器存入调度器时与其关联,用于排除触发调度中的特定时间段。例如,可创建一个每周一至周五上午9:30执行的触发器,再添加排除所有公司节假日的Calendar。
我的触发器是通过配置文件定义的,示例如下:
<trigger> <simple> <name>a trigger</name> <group>a trigger group</group> <description>a trigger description</description> <job-name>AJobName</job-name> <job-group>a job group</job-group> <job-data-map> <entry> <key>param1</key> <value>param1value</value> </entry> </job-data-map> <repeat-count>-1</repeat-count> <repeat-interval>300000</repeat-interval> </simple> </trigger>
想请教两个问题:
- 能否在不修改C#代码的情况下,让触发器在节假日日历指定的日期不执行?
- 如果不行,能否保留配置文件中的触发器定义,仅在代码中添加日历并关联到指定触发器?
注:不需要通过代码创建触发器并关联日历的方案。
解答
1. 纯配置文件实现的可能性
很遗憾,仅靠Quartz.NET的原生配置文件是做不到直接关联日历的。Quartz的XML配置(无论是核心配置文件还是自定义触发器配置文件)并没有提供直接配置Calendar并关联到触发器的节点或属性。Calendar的注册和关联逻辑目前只能通过代码层面完成,没办法完全脱离C#代码实现。
2. 保留配置文件触发器,代码中关联日历的方案
这个是完全可行的,而且能满足你保留现有配置定义的需求,具体步骤如下:
第一步:实现自定义节假日日历
先写一个继承BaseCalendar(或直接实现ICalendar接口)的自定义日历类,比如HolidayCalendar,在里面定义需要排除的节假日日期(可以从配置文件、数据库等外部源加载,避免硬编码)。示例代码:public class HolidayCalendar : BaseCalendar { private readonly HashSet<DateTime> _holidays = new HashSet<DateTime>(); public HolidayCalendar() { // 这里可以从配置文件、数据库读取节假日列表 _holidays.Add(new DateTime(2024, 1, 1)); _holidays.Add(new DateTime(2024, 2, 10)); // 其他节假日... } public override bool IsTimeIncluded(DateTimeOffset timeUtc) { DateTime localDate = timeUtc.LocalDateTime.Date; return !_holidays.Contains(localDate); } }第二步:在调度器初始化时关联日历与触发器
当你从配置文件加载完所有触发器之后,在调度器启动前,注册自定义日历,然后找到指定的触发器(通过配置里的名称和组),将两者关联起来。示例代码:// 假设已通过配置加载好调度器实例 IScheduler scheduler = await SchedulerFactory.GetDefaultScheduler(); // 注册自定义节假日日历,给日历命名为"holidayCalendar" ICalendar holidayCalendar = new HolidayCalendar(); await scheduler.AddCalendar("holidayCalendar", holidayCalendar, false, false); // 找到配置中定义的目标触发器 TriggerKey triggerKey = new TriggerKey("a trigger", "a trigger group"); ITrigger targetTrigger = await scheduler.GetTrigger(triggerKey); // 为触发器关联日历,设置为排除模式(日历中的日期不执行任务) targetTrigger = targetTrigger.GetTriggerBuilder() .ModifiedByCalendar("holidayCalendar") .Build(); // 更新调度器中的触发器 await scheduler.RescheduleJob(triggerKey, targetTrigger); // 启动调度器 await scheduler.Start();
这样一来,你完全保留了原有的配置文件触发器定义,只需要在调度器初始化的代码里添加几行逻辑,就能把指定触发器和节假日日历关联起来,实现节假日停止执行的需求。
内容的提问来源于stack exchange,提问作者Michel van Engelen

