spring boot集成定时任务
//看之前本博客是有项目地址的:
https://github.com/HouChenggong/springboot_schedule
项目地址: 传送门
定时任务或者说定时调度,是系统中比较普遍的一个功能,例如数据归档、清理,数据定时同步(非实时),定时收发,流量控制等等都需要用到定时任务,常见的定时调度框架有Quartz、TBSchedule等。
同样,Spring自3.0版本起也增加了任务调度功能Schedule,它好比是一个轻量级的Quartz,使用起来方便、简洁,且不需要依赖其他的JAR包。之所以说它是轻量级Quartz,是因为在现如今遍地分布式的大环境下,Spring自带的Schedule不支持分布式部署,所以若是分布式环境开发请忽略此文章,可以选用Quartz、TBSchedule等,且一般稍大点的公司都有独立的统一调度中心。
开始配置
1. 启动类添加定时任务注解
@EnableScheduling
@SpringBootApplication
//@MapperScan("com.pf.org.cms.mapper")
public class CmsApplication {
public static void main(String[] args) {
SpringApplication.run(CmsApplication.class, args);
}
}
2. 编写配置类
编写具体的定时任务组件(@Component注解),并且在需要定时调度的方法上添加@Scheduled触发器。Spring实现了Quartz简单的和cron表达式两种触发方式,这里我们用cron表达式,”0/20 * * * * ?”表示每20秒触发一次,具体cron表达式逻辑可以自行百度。
@Component
public class MyStaticTask {
/**
* 固定cron配置定时任务
*/
@Scheduled(cron = "0/20 * * * * ?")
public void doTask(){
System.out.println("执行了MyStaticTask,时间为:"+new Date(System.currentTimeMillis()));
}
}
以上,一个简单的定时任务便已完成,重新启动项目,你就会发现定时器了。
但是触发器是硬编码,相信肯定满足不了绝大部分业务场景,同样Spring提供了SchedulingConfigurer接口,下面介绍可配置、动态修改触发器的定时任务。
多线程定时任务
@Component
public class MyDynamicTask implements SchedulingConfigurer {
private static Logger log = LoggerFactory.getLogger(MyDynamicTask.class);
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
//设定一个长度10的定时任务线程池
//所有的定时任务都放在一个线程池中,定时任务启动时使用不同都线程。
scheduledTaskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
}
多线程定时任务结果日志
可以发现下面的定时任务都在任务池中,pool-1-thread-4、pool-1-thread-3、pool-1-thread-2
2018-11-05 11:55:00 284841 [pool-1-thread-4] INFO c.p.o.c.configuration.MyStaticTask - 定时任务1 开始执行...
2018-11-05 11:55:00 284841 [pool-1-thread-3] INFO c.p.o.c.configuration.MyStaticTask - 定时任务2 开始执行...
2018-11-05 11:55:00 284841 [pool-1-thread-1] INFO c.p.o.c.configuration.MyStaticTask - 定时任务0:执行了MyStaticTask,时间为:Mon Nov 05 11:55:00 CST 2018
2018-11-05 11:55:00 284841 [pool-1-thread-2] INFO c.p.o.c.configuration.MyDynamicTask - 执行了MyDynamicTask,时间为:Mon Nov 05 11:55:00 CST 2018
可配置的定时任务组件需要实现SchedulingConfigurer接口中的configureTasks方法,该方法有两个入参(Runnable task, Trigger trigger),第一个为我们任务的具体逻辑实现,第二个为触发器,动态的定时任务则意味着Trigger需要动态获取,由于之前我们已经集成redis,因此这里我们从redis获取相关配置。
//是从redis里面取数据,没有安装redis的可以参考这篇文章
。。。。
String newCron = redisManager.getStr("cms:MyDynamicTask");
package com.pf.org.cms.configuration;
import com.alibaba.druid.util.StringUtils;
import com.pf.org.cms.exception.CmsException;
import com.pf.org.cms.manage.RedisManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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.support.CronTrigger;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class MyDynamicTask implements SchedulingConfigurer {
private static Logger log = LoggerFactory.getLogger(MyDynamicTask.class);
@Autowired
RedisManager redisManager;
private static String cron;
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
scheduledTaskRegistrar.addTriggerTask(doTask(), getTrigger());
}
private Runnable doTask() {
return new Runnable() {
@Override
public void run() {
// 业务逻辑
log.info("执行了MyDynamicTask,时间为:" + new Date(System.currentTimeMillis()));
}
};
}
private Trigger getTrigger() {
return new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
// 触发器
CronTrigger trigger = new CronTrigger(getCron());
return trigger.nextExecutionTime(triggerContext);
}
};
}
public String getCron() {
String newCron = redisManager.getStr("cms:MyDynamicTask");
if (StringUtils.isEmpty(newCron)) {
throw new CmsException("The config cron expression is empty");
}
if (!newCron.equals(cron)) {
log.info(new StringBuffer("Cron has been changed to:'").append(newCron).append("'. Old cron was:'").append(cron).append("'").toString());
cron = newCron;
}
return cron;
}
}
安装redis并在redis的db2数据库中添加了
key:cms:MyDynamicTask
value:abcDEF
之后发生了如下的错误
Cron expression must consist of 6 fields
原因呢:其实就是你的value里面要是定时器的语法,我们的自定义语句中其实就是去redis里面去字符串,这个字符串是定时器脚本,而不是任意的这里贴出我的脚本,其实和最初的脚本一样
key:cms:MyDynamicTask
value:0/20 * * * * ?
项目地址:
https://github.com/HouChenggong/springboot_schedule