定时任务需求我们经常需要用到。在实现定时任务的场景中,一类是需要考虑到多节点只能由一个节点来执行的场景,这种定时任务需要能够将执行的记录存入到第三方存储中(比如数据库或者redis),实际的开发中是用一些定时任务的框架来完成(如quartz);另一类场景是每一个节点都能够执行的场景,不存在线程抢占的冲突。本文介绍的是适用于第二类场景,我们可以不用引入复杂的第三方框架,只用SpringBoot自带的方式来实现可以随时修改的定时任务。
实现用cron表达式的定时任务,我们可以用CronTrigger来实现,具体示例如下:
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-log4j2artifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.8version>
<optional>trueoptional>
dependency>
dependencies>
task.cron.print-time=0/1 * * * * ?
@EnableScheduling
@SpringBootApplication
public class SpringBootCronApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootCronApplication.class, args);
}
}
@Component
@Slf4j
@Data
@PropertySource("classpath:/task-config.properties")
public class CronScheduleTask implements SchedulingConfigurer {
@Value("${task.cron.print-time}")
private String cron;
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
// 动态使用cron表达式配置定时任务
taskRegistrar.addTriggerTask(() -> log.info(" current time : {}", LocalDateTime.now()), triggerContext -> {
// 使用CronTrigger触发器,可以动态的修改cron表达式来操作循环规则
CronTrigger cronTrigger = new CronTrigger(cron);
return cronTrigger.nextExecutionTime(triggerContext);
});
}
}
@Slf4j
@RestController
@RequestMapping("/test")
@AllArgsConstructor
public class ScheduleController {
private final CronScheduleTask cronScheduleTask;
//private final TimerScheduleTask timerScheduleTask;
@GetMapping("/updateCron")
public String updateCron(@RequestParam("cron") String cron) {
log.info("new cron : {}", cron);
cronScheduleTask.setCron(cron);
return "ok";
}
}
2023-05-13 18:09:44.016 INFO 50568 --- [pool-1-thread-1] c.e.s.shcedule.CronScheduleTask : current time : 2023-05-13T18:09:44.015
2023-05-13 18:09:45.013 INFO 50568 --- [pool-1-thread-1] c.e.s.shcedule.CronScheduleTask : current time : 2023-05-13T18:09:45.013
2023-05-13 18:09:46.005 INFO 50568 --- [pool-1-thread-1] c.e.s.shcedule.CronScheduleTask : current time : 2023-05-13T18:09:46.005
2023-05-13 18:09:47.001 INFO 50568 --- [pool-1-thread-1] c.e.s.shcedule.CronScheduleTask : current time : 2023-05-13T18:09:47.001
我们可以看到,打印定时任务每1s执行一次,接下来我们用浏览器调用以下url,将打印任务设置成每5s执行一次
http://localhost:8080/test/updateCron?cron=0/5%20*%20*%20*%20*%20?
执行结果如下:
2023-05-13 18:12:54.319 INFO 50568 --- [io-8080-exec-10] c.e.s.controller.ScheduleController : new cron : 0/5 * * * * ?
2023-05-13 18:12:54.319 INFO 50568 --- [pool-1-thread-1] c.e.s.shcedule.CronScheduleTask : current time : 2023-05-13T18:12:54.319
2023-05-13 18:12:55.002 INFO 50568 --- [pool-1-thread-1] c.e.s.shcedule.CronScheduleTask : current time : 2023-05-13T18:12:55.002
2023-05-13 18:13:00.006 INFO 50568 --- [pool-1-thread-1] c.e.s.shcedule.CronScheduleTask : current time : 2023-05-13T18:13:00.006
2023-05-13 18:13:05.015 INFO 50568 --- [pool-1-thread-1] c.e.s.shcedule.CronScheduleTask : current time : 2023-05-13T18:13:05.015
2023-05-13 18:13:10.002 INFO 50568 --- [pool-1-thread-1] c.e.s.shcedule.CronScheduleTask : current time : 2023-05-13T18:13:10.002
2023-05-13 18:13:15.008 INFO 50568 --- [pool-1-thread-1] c.e.s.shcedule.CronScheduleTask : current time : 2023-05-13T18:13:15.008
从打印结果中我们可以看到,定时任务被成功设置为每5s执行一次。
有时候,我们只需要实现一个每隔一定的时间就执行一次的定时任务,这个时候我们可以借助PeriodicTrigger来实现,PeriodicTrigger参数的单位为毫秒(ms),代码如下:
task.timer.print-time=1000
@Component
@Slf4j
@Data
@PropertySource("classpath:/task-config.properties")
public class TimerScheduleTask implements SchedulingConfigurer {
@Value("${task.timer.print-time}")
private Long timer;
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
// 动态使用cron表达式配置定时任务
taskRegistrar.addTriggerTask(() -> log.info("timer current time : {}", LocalDateTime.now()), triggerContext -> {
// 使用循环时间的触发器,可以随意设置循环的时间间隔
PeriodicTrigger periodicTrigger = new PeriodicTrigger(timer);
return periodicTrigger.nextExecutionTime(triggerContext);
});
}
}
@GetMapping("/updateTimer")
public String updateCron(@RequestParam("timer") Long timer) {
log.info("new timer : {}", timer);
timerScheduleTask.setTimer(timer);
return "ok";
}
2023-05-13 18:21:07.666 INFO 41852 --- [pool-1-thread-1] c.e.s.shcedule.TimerScheduleTask : timer current time : 2023-05-13T18:21:07.666
2023-05-13 18:21:08.681 INFO 41852 --- [pool-1-thread-1] c.e.s.shcedule.TimerScheduleTask : timer current time : 2023-05-13T18:21:08.681
2023-05-13 18:21:09.690 INFO 41852 --- [pool-1-thread-1] c.e.s.shcedule.TimerScheduleTask : timer current time : 2023-05-13T18:21:09.690
2023-05-13 18:21:10.702 INFO 41852 --- [pool-1-thread-1] c.e.s.shcedule.TimerScheduleTask : timer current time : 2023-05-13T18:21:10.702
我们可以看到,打印的定时任务每1s执行一次,接下来我们调用以下的url,修改为每5s执行一次
http://localhost:8080/test/updateTimer?timer=5000
结果如下:
2023-05-13 18:22:56.130 INFO 41852 --- [nio-8080-exec-1] c.e.s.controller.ScheduleController : new timer : 5000
2023-05-13 18:22:56.139 INFO 41852 --- [pool-1-thread-1] c.e.s.shcedule.TimerScheduleTask : timer current time : 2023-05-13T18:22:56.139
2023-05-13 18:23:01.140 INFO 41852 --- [pool-1-thread-1] c.e.s.shcedule.TimerScheduleTask : timer current time : 2023-05-13T18:23:01.140
2023-05-13 18:23:06.153 INFO 41852 --- [pool-1-thread-1] c.e.s.shcedule.TimerScheduleTask : timer current time : 2023-05-13T18:23:06.153
2023-05-13 18:23:11.154 INFO 41852 --- [pool-1-thread-1] c.e.s.shcedule.TimerScheduleTask : timer current time : 2023-05-13T18:23:11.154
2023-05-13 18:23:16.161 INFO 41852 --- [pool-1-thread-1] c.e.s.shcedule.TimerScheduleTask : timer current time : 2023-05-13T18:23:16.161
从执行结果可以看出,定时任务顺利被修改为每5s执行一次
后记
个人总结,欢迎转载、评论、批评指正