定时任务在很多场景有用,比如定时监控某服务,定时处理某处理等等,那么SpringBoot里面这么搞定时任务呢?下面我们来看看SpringBoot 定时任务的几种方式。

1.基于注解(最简单明了的一种)

来看看怎么注解,我们在service层定义个定时任务类吧,就叫ServiceJob:

@Component
@EnableScheduling //开启定时任务
public class ServiceJob {
    // 3秒执行一次
	@Scheduled(cron = "0/3 * * * * ?")
	public void runJob1() {
		System.out.println("runJob1--"+new Date());
	}
}

可以看到控制台每三秒打印一次,这就是最简单的注解定时任务了。Cron表达式参数分别表示如下:

@Scheduled:除了支持灵活的参数表达式cron之外,还支持简单的延时操作,例如 fixedDelay ,fixedRate 填写相应的毫秒数即可。如 Scheduled(fixedDelay = 3000 或fixedRate =3000)。注解的方式的确是太简单了,但是有个缺点,发现没有,如果我们每次要修改定时任务执行的频率,是不是要改注解里面的值,然后再重启服务。非常的不灵活。那怎么搞呢?解决办法就是基于接口。

2.基于数据库配置的定时任务

2.1、既然用到数据库,那我们就把数据库的依赖引入,如下:

  • 秒(0~59) 例如0/5表示每5秒
  • 分(0~59)
  • 时(0~23)
  • 日(0~31)的某天,需计算
  • 月(0~11)
  • 周几( 可填1-7 或 SUN/MON/TUE/WED/THU/FRI/SAT)
<dependency><!--添加MySql依赖 -->
             <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency><!--添加Mybatis依赖 配置mybatis的一些初始化的东西-->
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency><!-- 添加mybatis依赖 -->
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
            <scope>compile</scope>
        </dependency>

 2.2、创建DynamicServiceJob如下:

@Component
@EnableScheduling
public class DynamicServiceJob implements SchedulingConfigurer{
	// 注入mapper
    @Autowired
	CronMapper cronMapper;
	public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
		
		taskRegistrar.addTriggerTask(
		// 1.添加任务内容
	    () -> System.out.println("DynamicServiceJob执行定时任务"+ new Date())
        , 
        // 2设置执行周期
        triggerContext -> {
        	// 2.1 从数据库获取执行周期
        	String corn = cronMapper.getCorn();
        	// 2.2校验 corn是否合法
        	if(StringUtils.isEmpty(corn)) {
        		System.out.println("配置的定时任务频率为空!");
        	}
        	// 2.3 返回执行周期
        	return new CronTrigger(corn).nextExecutionTime(triggerContext);
        }
        );
	}

}

2.3、创建我们的mapper:

@Mapper
public interface CronMapper {

	@Select("select cron from cron where cron_id = 1")
	public String getCorn();
}

2.4 、application.yml配置数据库:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/scheduled?serverTimezone=GMT%2B8
    username: root

2.5、创建我们的数据库表并插入数据:

CREATE TABLE `cron`  (
  `cron_id` varchar(30) NOT NULL PRIMARY KEY,
  `cron` varchar(30) NOT NULL  
);
INSERT INTO `cron` VALUES ('1', '0/2 * * * * ?');
Query OK, 0 rows affected

Query OK, 1 row affected

 

微服务集群定时任务 微服务中的定时任务_数据库

2.6、跑起来看看

微服务集群定时任务 微服务中的定时任务_数据库_02

数据库改为5秒:

 

微服务集群定时任务 微服务中的定时任务_定时任务_03

再来看看效果:

 

微服务集群定时任务 微服务中的定时任务_定时任务_04

成功!这里需要注意的是如果在数据库修改时格式出现错误,则定时任务会停止。即使重新修改正确,也只能重新启动项目才能恢复。

3.异步定时任务

3.1、首先我们来看看非异步定时的效果,创建两定时任务,如下:

// 2秒执行一次
	@Scheduled(cron = "0/2 * * * * ?")
	public void runJob1() {
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + "runJob1--" + new Date());
	}

	// 1秒执行一次
	@Scheduled(cron = "0/1 * * * * ?")
	public void runJob2() {
		
		System.out.println(Thread.currentThread().getName() +"runJob2--" + new Date());
	}

结果如下:

微服务集群定时任务 微服务中的定时任务_数据库_05

可以看到,定时任务都在一个线程里面,且相互之间有影响,那怎么解决呢?答案就是异步(也就是多线程),来看看怎么实现

3.2、异步定时任务

@Component
@EnableScheduling // 1.开启定时任务
@EnableAsync        // 2.开启多线程
public class ServiceJob {
	// 2秒执行一次
	@Async
	@Scheduled(cron = "0/2 * * * * ?")
	public void runJob1() {
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + "runJob1--" + new Date());
	}

	// 1秒执行一次
	@Async
	@Scheduled(cron = "0/1 * * * * ?")
	public void runJob2() {
		
		System.out.println(Thread.currentThread().getName() +"runJob2--" + new Date());
	}
}

来看看执行效果:

微服务集群定时任务 微服务中的定时任务_System_06

可以看到,定时任务在不同的线程中执行,且相互间不影响。