重点是动态,cron规则保存在数据库,通过定时job去刷新数据库,获取到变更后的规则后重新刷新job,不废话,用到的人肯定知道做什么的,主要代码如下:
@Component
@EnableScheduling
@Slf4j
public class DynamicCronScheduler implements SchedulingConfigurer {
@Autowired
EventSubRuleFactory eventSubRuleFactory;
@Autowired
CronJobEventSubscriber eventSubscriber;
@Autowired
EventSubRuleMapper eventSubRuleMapper;
private ScheduledTaskRegistrar taskRegistrar;
@Autowired
RedissonClient redisClient;
private String md5;
@Autowired
ThreadPoolTaskScheduler threadPoolTaskScheduler;
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
this.taskRegistrar = taskRegistrar;
taskRegistrar.setScheduler(threadPoolTaskScheduler);
if (CollectionUtils.isEmpty(taskRegistrar.getTriggerTaskList())) {
fetchCronJobs();
}
}
/**
* 定时刷新动态调度器job
* 每小时执行一次
*/
@Scheduled(cron = "0 0 */1 * * ?")
public void fetchCronJobs() {
List<EventSubRule> cornJobEventSubRules = eventSubRuleFactory.getAllCornJobEventSubRule();
Optional<String> result = cornJobEventSubRules.stream().sorted((o1, o2) -> (int) (o1.getId() - o2.getId())).map(EventSubRule::getTopic)
.reduce((a, b) -> a + b);
String newMd5 = DigestUtils.md5Hex(result.orElse(""));
if (newMd5.equals(md5)) {
return;
}
if (this.taskRegistrar == null) {
return;
}
// 定时触发,先清空,取消已经在执行的task
if (CollectionUtils.isNotEmpty(taskRegistrar.getTriggerTaskList())) {
Set<ScheduledTask> tasks = taskRegistrar.getScheduledTasks();
for (ScheduledTask task : tasks) {
task.cancel();
}
try {
Field field = ScheduledTaskRegistrar.class.getDeclaredField("scheduledTasks");
field.setAccessible(true);
Set<ScheduledTask> scheduledTasks = (Set<ScheduledTask>) field.get(taskRegistrar);
scheduledTasks.clear();
} catch (Exception e) {
log.error("scheduledTasks.clear failed ", e);
return;
}
log.info("cron job flush start ");
} else {
log.info("cron job firstly fetch ");
}
try {
// 查询所有的job任务
List<TriggerTask> triggerTasks = new ArrayList<>();
for (EventSubRule eventSubRule : cornJobEventSubRules) {
AtomicBoolean hasError = new AtomicBoolean(false);
Trigger trigger = triggerContext -> {
try {
String cron = eventSubRule.getTopic();
log.info("cron job triggered subject:{}, expression:{}", eventSubRule.getSubject(), cron);
CronTrigger cronTrigger = new CronTrigger(cron);
return cronTrigger.nextExecutionTime(triggerContext);
} catch (Exception e) {
log.info("cron job is not valid expression:{}", eventSubRule.getTopic());
hasError.set(true);
return null;
}
};
if (hasError.get()) {
continue;
}
Runnable task = () -> {
RLock redissonLock = redisClient.getLock(KeyUtil.buildLockKey("DynamicCronScheduler-" + eventSubRule.getId()));
try {
if (!redissonLock.tryLock(0, 5, TimeUnit.SECONDS)) {
return;
}
} catch (InterruptedException e) {
return;
}
Event cronJobEvent = new Event();
Map<String, Object> context = new HashMap<>();
Date now = new Date();
context.put("hour", DateUtil.hourOf(now));
context.put("day", DateUtil.day(now));
context.put("minute", DateUtil.minute(now));
context.put("second", DateUtil.second(now));
context.put("dateTimeSec", DateUtil.dateTimeSec(now));
context.put("dateTime", DateUtil.dateTime(now));
cronJobEvent.setMsgBody(context);
cronJobEvent.setEventId(IdUtil.simpleUUID());
cronJobEvent.setSubject(eventSubRule.getSubject());
cronJobEvent.setTimestamp(now.getTime());
try {
eventSubscriber.consume(cronJobEvent);
} catch (Exception e) {
log.error("DynamicCronSchedule run task failed,", e);
}
};
triggerTasks.add(new TriggerTask(task, trigger));
}
this.taskRegistrar.setTriggerTasksList(triggerTasks);
if (StringUtils.isNotEmpty(md5)) {
// 第一次启动不需要调用afterPropertiesSet
taskRegistrar.afterPropertiesSet();
}
md5 = newMd5;
log.info("dynamic trigger list size is {}", taskRegistrar.getTriggerTaskList().size());
} catch (Exception e) {
log.error("configure DynamicCronScheduleTasks failed,", e);
}
}
}