SpringBoot如何配置动态cron任务
前情回顾
SpringBoot 通过 @Scheduled
注解提供了cron、fixedRate、fixedDelay三种定时任务执行规则。
举个例子,比如每5秒执行一次任务,你可以使用下面三种方式:
- @Scheduled(fixedRate = 5000)
- @Scheduled(fixedDelay = 5000)
- @Scheduled(cron = “0/5 * * * * *”)
下面我们来看一下每种方式的执行区别是什么:
- fixedRate 是按照一定的速率执行,是从上一次方法执行开始的时间算起,如果上一次方法阻塞住了,下一次也是不会执行,但是在阻塞这段时间内累计应该执行的次数,当不再阻塞时,一下子把这些全部执行掉,而后再按照固定速率继续执行。
- fixedDelay 控制方法执行的间隔时间,是以上一次方法执行完开始算起,如上一次方法执行阻塞住了,那么直到上一次执行完,并间隔给定的时间后,执行下一次。
- cron 表达式可以定制化执行任务,但是执行的方式是与 fixedDelay 相近的,也是会按照上一次方法结束时间开始算起。
进入正文
说一说我遇到的问题,@Scheduled
注解天生无法使用变量,如果你用了,会提升你 Attribute value must be constant。无奈只能使用编程的方式进行定时任务的开发。
SpringBoot 显然已经考虑到了,提供了一个 SchedulingConfigurer
接口用来在任务执行时进行动态修改。我已经封装好了一个实现类,使用时只需要注入该 Bean 即可。
代码如下:
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.config.TriggerTask;
import org.springframework.scheduling.support.CronTrigger;
import java.util.Date;
/**
* 动态Cron任务
* 注入Spring容器使用
* @date 2020/12/30
*/
public class CronScheduledBean implements SchedulingConfigurer {
private static final int HEART_BEAT = 60 * 1000;
private String cron;
private TriggerTask triggerTask;
private ScheduledTaskRegistrar scheduledTaskRegistrar;
private Date nextDate;
/**
* 配置任务逻辑和cron表达式
* @param cronTask 任务逻辑
* @param cron 表达式
*/
public CronScheduledBean(Runnable cronTask, String cron) {
this.cron = cron;
this.triggerTask = new TriggerTask(doTask(cronTask), getTrigger());
}
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
this.scheduledTaskRegistrar = scheduledTaskRegistrar;
scheduledTaskRegistrar.addTriggerTask(triggerTask);
}
/**
* 动态修改cron表达式
*/
public void updateCron(String cron) {
this.cron = cron;
this.scheduledTaskRegistrar.scheduleTriggerTask(triggerTask);
}
/**
* 任务执行方法
*/
private Runnable doTask(Runnable cronTask) {
return new Runnable() {
@Override
public void run() {
// 存在乱触发情况,需要判断是否在可执行时间内
if (System.currentTimeMillis() >= nextDate.getTime()) {
cronTask.run();
}
}
};
}
/**
* 任务触发器
*/
private Trigger getTrigger() {
return new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
// 根据触发器上下文获取下一次执行的时间
try {
nextDate = new CronTrigger(cron).nextExecutionTime(triggerContext);
} catch (Exception e) {
// 出现异常过1分钟再检查,可以自行修改监测时间
nextDate = new Date(System.currentTimeMillis() + HEART_BEAT);
}
System.out.println("触发器:"+nextDate);
return nextDate;
}
};
}
}
使用示例:
@RestController
public class TaskController {
@Autowired
private CronScheduledBean cronScheduledBean;
@Bean
public CronScheduledBean scheduledUtil() {
return new CronScheduledBean(new Runnable() {
@Override
public void run() {
System.out.println("任务执行:" + new Date());
}
}, "*/5 * * * * ?");
}
@GetMapping("/editCron")
public String editCron(String cron) {
System.out.println("修改: " + cron + " " + new Date());
cronScheduledBean.updateCron(cron);
return cron;
}
}
注意,分布式应用慎用。
版权声明:凡未经本网站书面授权,任何媒体、网站及个人不得转载、复制、重制、改动、展示或使用本网站的局部或全部的内容或服务,或在非本网站所属服务器上建立镜像。如果已转载,请自行删除。同时,我们保留进一步追究相关行为主体的法律责任的权利。我们希望与各媒体合作,签订著作权有偿使用许可合同,故转载方须书面/邮件申请,以待商榷。