Spring Boot 2.X(十二):定時任務

簡介

定時任務是後端開發中常見的需求,主要應用場景有按期數據報表、定時消息通知、異步的後臺業務邏輯處理、日誌分析處理、垃圾數據清理、定時更新緩存等等。java

Spring Boot 集成了一整套的定時任務工具,讓咱們專一於完成邏輯,剩下的基礎調度工做將自動完成。git

通用實現方式

實現方式 描述
java.util.Timer Timer 提供了一個 java.util.TimerTask 任務支持任務調度。該方式只能按指定頻率執行,不能在指定時間運行。因爲功能過於單一,使用較少。
Quartz Quartz 是一個功能比較強大的調度器,支持在指定時間運行,也能夠按照指定頻率執行。缺點是使用起來相對麻煩。
Spring 框架自帶的 Schedule 模塊 能夠看作是輕量級的 Quartz

靜態定時任務

@Component
@EnableScheduling
public class StaticScheduleJob {
	
	/**
	 * 上次開始執行時間點後5秒再次執行
	 */
	@Scheduled(fixedRate = 5000)
	public void job3() {
		System.out.println("執行任務job3:"+DateUtil.formatDateTime(new Date()));
	}
	
	/**
	 * 上次執行完畢時間點後3秒再次執行
	 */
	@Scheduled(fixedDelay = 3000)
	public void job2() {
		System.out.println("執行任務job2:"+DateUtil.formatDateTime(new Date()));
	}
	
	/**
	 * 每隔10秒執行一次(按照 corn 表達式規則執行)
	 */
	@Scheduled(cron = "0/10 * * * * ?")
	public void job1() {
		System.out.println("執行任務job1:"+DateUtil.formatDateTime(new Date()));
	}

	
}

複製代碼

@EnableScheduling 註解啓用定時調動功能github

@Scheduled 參數說明:spring

  • @Scheduled(fixedRate = 5000):上次開始執行時間點後5秒再次執行
  • @Scheduled(fixedDelay = 3000):上次執行完畢時間點後3秒再次執行
  • @Scheduled(cron = "0/10 * * * * ?"):每隔10秒執行一次(按照 corn 表達式規則執行)

Cron 表達式

1.Cron表達式格式

{秒} {分} {時} {日} {月} {周} {年(可選)}數據庫

2.Cron 表達式字段取值範圍及說明

字段 取值範圍 容許的特殊字符
Seconds(秒) 0 ~ 59 , - * /
Minutes(分) 0 ~ 59 , - * /
Hours(時) 0 ~ 23 , - * /
Day-of-Month(天) 能夠用數字 1 ~ 31 中的任意一個值,但要注意特殊月份 , - * ? / L W C
Month(月) 能夠用 0 ~ 11 或者字符串 「JAN、FEB、MAR、APR、MAY、JUN、JUL、AUG、SEP、OCT、NOV、DEC」 表示 , - * /
Day-of-Week(每週) 能夠用數字 1 ~ 7 表示(1=星期日)或者用字符串 「SUN、MON、TUE、WED、THU、FRI、SAT」 表示 , - * ? / L C #
Year(年) 取值範圍(1970-2099),容許爲空值 , - * /

3.Cron 表達式中特殊字符的意義

特殊字符 說明
* 表示能夠匹配該域的全部值
主要用於日和星期,能夠匹配域的任意值,但實際不會。當2個子表達式其中之一被指定了值之後,爲了不衝突,須要將另外一個子表達式的值設爲?
/ 表示爲每隔一段時間。如 0 0/10 * * * ? 其中的 0/10表示從0分鐘開始,每隔10分鐘執行一次
- 表示範圍。如 0 0-5 14 * * ? 表示在天天14:00到14:05期間每1分鐘執行一次
, 表示枚舉多個值,這些值之間是"或"的關係。如 0 10,30 14 * 3 ? 表示每一個星期二14點10分或者14點30分執行一次
L 表示每個月或者每週的最後一天。如 0 0 0 L * ? * 表示每個月的最後一天執行
W 表示最近工做日。如 0 0 0 15W * ?* 表示每個月15號最近的那個工做日執行
# 用來指定具體的週數,"#"前面表明星期,"#"後面表明本月的第幾周。如"2#1"表示本月第二週的星期日

4.Cron 在線生成工具

www.bejson.com/othertools/…json

動態定時任務

1.實現 SchedulingConfigurer 接口

@Configuration
public class CustomScheduleConfig implements SchedulingConfigurer {

	@Autowired
	private CronTriggerDao cronTriggerDao;

	@Override
	public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
		// 上次開始執行時間點後5秒再執行
		taskRegistrar.addFixedRateTask(() -> System.out.println("CustomScheduleConfig執行任務job1=>"
				+ DateUtil.formatDateTime(new Date()) + ",線程:" + Thread.currentThread().getName()), 5000);
		// 上次執行完畢時間點後3秒再執行
		taskRegistrar.addFixedDelayTask(() -> System.out.println("CustomScheduleConfig執行任務job2=>"
				+ DateUtil.formatDateTime(new Date()) + ",線程:" + Thread.currentThread().getName()), 3000);
		// 添加一個配合數據庫動態執行的定時任務
		TriggerTask triggerTask = new TriggerTask(
				// 任務內容.拉姆達表達式
				() -> {
					// 1)添加任務 Runnable
					System.out.println("CustomScheduleConfig執行動態任務job3=>" + DateUtil.formatDateTime(new Date()) + ",線程:"
							+ Thread.currentThread().getName());
					// 2)設置執行週期
				}, triggerContext -> {
					// 3)從數據庫獲取執行週期
					String cron = cronTriggerDao.getCronTriggerById(1L);
					if (cron != null) {
						// 4)返回執行週期(Date)
						return new CronTrigger(cron).nextExecutionTime(triggerContext);
					} else {
						return null;
					}

				});
		taskRegistrar.addTriggerTask(triggerTask);

	}

}
複製代碼

2.數據庫中初始化數據

這邊爲了測試簡單初始化兩行數據:後端

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_cron_trigger
-- ----------------------------
DROP TABLE IF EXISTS `t_cron_trigger`;
CREATE TABLE `t_cron_trigger` (
  `id` int(8) NOT NULL AUTO_INCREMENT COMMENT '任務id',
  `cron` varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT 'cron表達式',
  `create_time` datetime DEFAULT NULL COMMENT '建立時間',
  `update_time` datetime DEFAULT NULL COMMENT '更新時間',
  `is_deleted` int(1) DEFAULT '0' COMMENT '做廢狀態 0-否 1-是',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

-- ----------------------------
-- Records of t_cron_trigger
-- ----------------------------
BEGIN;
INSERT INTO `t_cron_trigger` VALUES (1, '0/10 * * * * ?', '2019-10-30 13:40:14', NULL, 0);
INSERT INTO `t_cron_trigger` VALUES (2, '0/7 * * * * ?', '2019-10-30 13:40:34', NULL, 0);
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

複製代碼

3.從數據庫中讀取 cron 表達式值

CronTrigger 實體類緩存

public class CronTrigger implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = 880141459783509786L;
	
	private Long id;//任務id
	private String cron;//cron表達式
	private String createTime;//建立時間
	private String updateTime;//更新時間
	private boolean isDeleted=false;//刪除狀態
	
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getCron() {
		return cron;
	}
	public void setCron(String cron) {
		this.cron = cron;
	}
	public String getCreateTime() {
		return createTime;
	}
	public void setCreateTime(String createTime) {
		this.createTime = createTime;
	}
	public String getUpdateTime() {
		return updateTime;
	}
	public void setUpdateTime(String updateTime) {
		this.updateTime = updateTime;
	}
	public boolean isDeleted() {
		return isDeleted;
	}
	public void setDeleted(boolean isDeleted) {
		this.isDeleted = isDeleted;
	}
}
複製代碼

CronTriggerDaospringboot

public interface CronTriggerDao {
	/**
	 * 根據id獲取cron表達式
	 * @param id
	 * @return
	 */
	String getCronTriggerById(Long id);

}

複製代碼

CronTriggerMapper.xmlbash

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.4//EN" 
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.zwqh.springboot.dao.CronTriggerDao">
	<resultMap type="cn.zwqh.springboot.model.CronTrigger" id="cronTrigger">
		<id property="id" column="id"/>
		<result property="cron" column="cron"/>
		<result property="createTime" column="create_time"/>
		<result property="updateTime" column="update_time"/>
		<result property="isDeleted" column="is_deleted"/>
	</resultMap>
	<!-- 根據id獲取cron表達式 -->
	<select id="getCronTriggerById" resultType="String">
		select cron from t_cron_trigger where is_deleted=0 and id=#{id}
	</select>
</mapper>


複製代碼

4.測試

啓動前記得在啓動類上加上 @EnableScheduling

打印日誌以下:

多線程定時任務

經過上面的日誌咱們能夠看到任務執行都是單線程的。若是要實現多線程執行任務,咱們能夠經過在 SchedulingConfigurer 接口的 configureTasks方法中添加線程池便可。

1.CustomScheduleConfig

@Configuration
public class CustomScheduleConfig implements SchedulingConfigurer {

	@Autowired
	private CronTriggerDao cronTriggerDao;

	@Override
	public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
		
		//設定一個長度爲10的定時任務線程池
		taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
		
		// 上次開始執行時間點後5秒再執行
		taskRegistrar.addFixedRateTask(() -> System.out.println("CustomScheduleConfig執行任務job1=>"
				+ DateUtil.formatDateTime(new Date()) + ",線程:" + Thread.currentThread().getName()), 5000);
		// 上次執行完畢時間點後3秒再執行
		taskRegistrar.addFixedDelayTask(() -> System.out.println("CustomScheduleConfig執行任務job2=>"
				+ DateUtil.formatDateTime(new Date()) + ",線程:" + Thread.currentThread().getName()), 3000);
		// 添加第一個配合數據庫動態執行的定時任務
		TriggerTask triggerTask = new TriggerTask(
				// 任務內容.拉姆達表達式
				() -> {
					// 1)添加任務 Runnable
					System.out.println("CustomScheduleConfig執行動態任務job3=>" + DateUtil.formatDateTime(new Date()) + ",線程:"
							+ Thread.currentThread().getName());
					// 2)設置執行週期
				}, triggerContext -> {
					// 3)從數據庫獲取執行週期
					String cron = cronTriggerDao.getCronTriggerById(1L);
					if (cron != null) {
						// 4)返回執行週期(Date)
						return new CronTrigger(cron).nextExecutionTime(triggerContext);
					} else {
						return null;
					}

				});
		taskRegistrar.addTriggerTask(triggerTask);
		// 添加第二個配合數據庫動態執行的定時任務
		TriggerTask triggerTask2 = new TriggerTask(
				// 任務內容.拉姆達表達式
				() -> {
					// 1)添加任務 Runnable
					System.out.println("CustomScheduleConfig執行動態任務job4=>" + DateUtil.formatDateTime(new Date()) + ",線程:"
							+ Thread.currentThread().getName());
					// 2)設置執行週期
				}, triggerContext -> {
					// 3)從數據庫獲取執行週期
					String cron = cronTriggerDao.getCronTriggerById(2L);
					if (cron != null) {
						// 4)返回執行週期(Date)
						return new CronTrigger(cron).nextExecutionTime(triggerContext);
					} else {
						return null;
					}

				});
		taskRegistrar.addTriggerTask(triggerTask2);

	}

}
複製代碼

2.測試

打印日誌以下:

示例代碼

github

碼雲

非特殊說明,本文版權歸 朝霧輕寒 全部,轉載請註明出處.

原文標題:Spring Boot 2.X(十二):定時任務

原文地址: https://www.zwqh.top/article/info/21

若是文章對您有幫助,請掃碼關注下個人公衆號,文章持續更新中...

相關文章
相關標籤/搜索