Spring Boot 2.X(十二):定時任務
- 2019 年 10 月 30 日
- 筆記
簡介
定時任務是後端開發中常見的需求,主要應用場景有定期數據報表、定時消息通知、異步的後台業務邏輯處理、日誌分析處理、垃圾數據清理、定時更新緩存等等。
Spring Boot 集成了一整套的定時任務工具,讓我們專註於完成邏輯,剩下的基礎調度工作將自動完成。
通用實現方式
實現方式 | 描述 |
---|---|
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 註解啟用定時調動功能
@Scheduled 參數說明:
- @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 在線生成工具
http://www.bejson.com/othertools/cron/
動態定時任務
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; } }
CronTriggerDao
public interface CronTriggerDao { /** * 根據id獲取cron表達式 * @param id * @return */ String getCronTriggerById(Long id); }
CronTriggerMapper.xml
<?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.測試
打印日誌如下:
示例代碼
非特殊說明,本文版權歸 朝霧輕寒 所有,轉載請註明出處.
原文標題:Spring Boot 2.X(十二):定時任務
原文地址: https://www.zwqh.top/article/info/21
如果文章對您有幫助,請掃碼關注下我的公眾號,文章持續更新中…