#IT明星不是夢#【從0開始Web開發實戰】SpringBoot集成Quartz實現定時任務和調度

目錄:

1. SpringBoot集成Quartz

2. 實現定時任務邏輯

3. 動態創建定時任務

4. Quartz常用類

5. Quartz任務熄火策略

6. Cron表達式速成

7. Quartz使用技巧之通過JobDataMap和SchedulerContext傳遞數據

8. Spring框架Schedule功能的使用方法


Quartz是開源組織OpenSymphony的一個作業調度框架,採用多線程架構,可伸縮性強,可集羣擴展。SpringBoot集成Quartz簡單高效,只需實現Job接口,在方法execute()中添加業務邏輯


Spring框架自帶了Schedule功能,能滿足小型項目對定時任務的功能需求,同樣支持Cron表達式、固定間隔、固定頻率三種配置方式,但和Quartz相比,不支持任務信息數據庫持久化。


Cron表達式

固定間隔

固定頻率

傳遞數據

任務持久化

Quartz

Spring Schedule

×

×


本文分享SpringBoot集成和配置Quartz的方法,以及在項目中的實際應用方案,最後介紹Spring 框架自帶的Schedule功能的用法。



代碼文件

功能要點

SpringBoot集成Quartz

pom.xml

引入Quartz依賴spring-boot-starter-quartz

application.yml

配置Quartz屬性,配置Job運行時間cron表達式

QuartzConfig.java

配置Bean: JobDetail, Trigger,讀取cron運行時間配置

實現定時任務

QuartzJob.java

實現Job接口,或者繼承QuartzJobBean類

動態創建任務

CheckController.java

增加REST接口/chk/job,創建一個Job定時任務,通過Context上下文傳遞數據。


項目代碼:https://github.com/jextop/StarterApi/

示例代碼:https://github.com/rickding/HelloJava/tree/master/HelloQuartz


一,SpringBoot集成Quartz

1. 新建SpringBoot項目時,選中Quartz,將自動添加Quartz依賴。

image.png

2. 已有SpringBoot項目,可以在pom.xml中直接添加Quartz依賴。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

3. application.yml中配置Quartz,指定任務存儲類型:

memory:內存方式,默認。

jdbc:數據庫持久化方式,將創建數據表並保存任務信息。

spring:
  quartz:
    job-store-type: jdbc
    jdbc:
      initialize-schema: always

 

job:
  quartz:
    cron: 0 0/23 * * * ?

4. 在QuartzConfig.java中配置Bean,聲明JobDetail和Trigger,使用cron表達式設置任務運行時間:

@Configuration
@ConfigurationProperties("job.quartz")
public class QuartzConfig {
    private String cron;

    @Bean
    public JobDetail quartzJob() {
        return JobBuilder.newJob(QuartzJob.class).storeDurably().build();
    }

    @Bean
    public Trigger quartzTrigger() {
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);

        return TriggerBuilder.newTrigger()
                .forJob(quartzJob())
                .withSchedule(scheduleBuilder)
                .build();
    }

    public String getCron() {
        return cron;
    }

    public void setCron(String cron) {
        this.cron = cron;
    }
}


二,定時任務QuartzJob.java,繼承QuartzJobBean類實現Job接口。

JobExecutionContext中讀取附加信息,執行業務邏輯。

image.png 

public class QuartzJob extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        LogUtil.info("quartz job ", new Date());

        try {
            JextService jextService = (JextService) context.getScheduler().getContext().get("jextService");
            if (jextService != null) {
                jextService.getInfo(true);
            }
        } catch (SchedulerException e) {
            LogUtil.info(e.getMessage());
        }
    }
}


三,動態創建定時任務

1. 增加RestController:CheckController.java

2. 增加REST接口/chk/job,創建一個定時任務,添加附加信息到Scheduler上下文中。

@GetMapping(value = "/chk/job")
public Object job() {
    JobDetail job = JobBuilder.newJob(QuartzJob.class).build();
    SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
            .forJob(job)
            .startAt(new Date())
            .build();

    Date date = null;
    try {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        scheduler.getContext().put("jextService", jextService);

        date = scheduler.scheduleJob(job, trigger);
        scheduler.startDelayed(1);
    } catch (SchedulerException e) {
        e.printStackTrace();
    }

    final Date jobDate = date;
    return new HashMap<String, Object>() {{
        put("chk", "job");
        put("date", jobDate);
    }};
}

3. Postman調用REST接口創建定時任務示例

image.png 

四,Quartz常用類

Quartz提供的常用類:Scheduler, SchedulerFactory, Job, JobDetail, JobBuilder, Trigger, TriggerBuilder, ScheduleBuilder,UML類圖如下:

image.png

Scheduler時Quartz調度程序的主要接口,維護一個JobDetails和Triggers的註冊表,到觸發時間時,調度程序將執行Job。


調度程序Scheduler實例通過SchedulerFactory工廠創建,有兩個實現類DirectSchedulerFactory和StdSchedulerFactory,前者不寫入持久化數據庫,後者加載quartz.properties屬性配置文件,將查找加載當前目錄和org.quartz包。


任務是實現Job接口的一個類,實現方法execute(),可聲明屬性:

- @DisallowConcurrentExecution,同時只執行一個實例。

- @PersisJobDataAfterExecution,正常執行後將JobDataMap備份。

JobDetail將任務屬性傳遞給Scheduler,通過JobBuilder創建,JobDataMap保存任務實例的狀態信息。


觸發器Trigger通過TriggerBuilder創建,結合ScheduleBuilder設置時間規則,可通過JobDataMap傳遞數據給Job。常用的兩種觸發器:

- SimpleTrigger:指定開始時間、結束時間、重複次數、執行間隔。

- CronTrigger:使用Cron表達式設置時間規則。


五,Quartz任務熄火策略

ScheduleBuilder設置時間規則時,可配置Misfire選項,指定執行失敗熄火時的處理規則:


含義

支持ScheduleBuilder

IgnoreMisfires

馬上執行,比如整點9點失敗,系統10:15啓動,會馬上執行9點10點的任務。

SimpleSchedule

CronScheduleBuilder

CalendarIntervalScheduleBuilder

DailyTimeIntervalScheduleBuilder

FireNow

立即再次觸發

SimpleSchedule

NowWithExistingCount

立即再次觸發,不計入總次數。

SimpleSchedule

NowWithRemainingCount

立即再次觸發,計入總次數。

SimpleSchedule

NextWithExistingCount

等待下次執行,不計入總次數。

SimpleSchedule

NextWithRemainingCount

等待下次執行,計入總次數。

SimpleSchedule

DoNothing

不做任何處理,執行下一次週期。

CronScheduleBuilder

CalendarIntervalScheduleBuilder

DailyTimeIntervalScheduleBuilder

FireAndProceed

合併下一個週期,正常執行。比如整點9點10點失敗,系統10:15啓動,在11點合併執行一次。

CronScheduleBuilder

CalendarIntervalScheduleBuilder

DailyTimeIntervalScheduleBuilder


六,Cron表達式速成

Cron表達式是一個字符串,定義時間規則,由6或7個時間域組成,空格分隔1張表整理清楚含義和規則,並舉例常用表達式,放手邊速查。

時間域序號

含義

取值範圍

特殊字符

1

秒Seconds

0-59

,-*/

2

分鐘Minutes

0-59

,-*/

3

小時Hours

0-23

,-*/

4

日期DayOfMonth

1-31

,-*/ ? L W C

5

月份Month

1-12

,-*/ JAN-DEC

6

星期DayOfWeek

1-7

,-*/ ? L C # SUN-SAT

7

年Year (可選)

1970-2099

,-*/

特殊字符含義

JAN-DEC 月份英語簡稱

SUN-SAT 星期英語簡稱

星期的1表示星期天,2表示星期一,依次類推

* 表示取值範圍內的所有數字

/ 表示每隔固定時間觸發依次,比如0/5表示從0開始每5個單位時間

- 表示兩個數字之間的範圍,比如3-7表示3到7之間,包含3和7

, 表示離散的枚舉數字,比如2,3,5,7表示指定的這幾個時間

? 只能用在日期DayOfMonth和星期DayOfWeek兩個域,表示不指定,避免日期和星期的互相影響,比如指定每月的20日,不管是星期幾,正確寫法是:0 0 0 20 * ?,其中星期只能用?,如果使用*將觸發錯誤。

L 只能用於日期DayOfMonth和星期DayOfWeek,用於日期時表示月份的最後一天,用於星期時不加數字表示週六,加數字表示最後一個周幾,比如0 0 0 ? * 5L表示每月的最後一個星期四

W 只能用於日期DayOfMonth,表示週一到週五有效工作日,將在離指定日期的最近的有效工作日觸發事件。例如在日期使用5W,如果5日是星期六,則將在最近的工作日星期五(4日)觸發。如果5日是星期天,則在6日(星期一)觸發;如果5日在星期一到星期五中的一天,則就在5日觸發。另外一點,W的最近工作日尋找不會跨月份。

LW 兩個字符連用時表示某個月最後一個工作日

# 只能用於星期DayOfWeek,表示每個月第幾個星期幾,比如4#2表示第二個星期三

常用表達式

0/5 * * * * ? 每5秒鐘

0 0/5 * * * ? 每5分鐘

0 0 6 * * ? 每天早上6點

0 0 9,13,19 * * ? 每天上午9點,下午1點,晚上7點

0 0 23-7/2,8 * * ? 每天晚上11點到早上7點之間的每兩個小時,和早上8點

0 0/30 9-17 * * ? 朝九晚五工作時間內每半小時

0 0 9-21 ? * MON-SAT 表示996每天的每小時

0 0 7 LW * ? 每月最後一個工作日早上7點

0 0 4 1 1 ? 每年的1月1日早上4點


七,Quartz使用技巧之通過JobDataMap和SchedulerContext傳遞數據

Job.execute()方法中實現業務邏輯時,經常需要一些附加信息。Quartz提供了JobExecutionContext上下文傳遞數據。

image.png 

通過JobDataMap傳遞數據

代碼下載:https://github.com/rickding/HelloJava/tree/master/HelloQuartz

src/main/java/com/hello/quartz/

├── QuartzConfig.java

├── QuartzJob.java


代碼文件

功能要點

設置數據

QuartzConfig.java

創建JobDetail或者Trigger時,調用usingJobData()設置數據

讀取數據

QuartzJob.java

執行任務時,調用JobExecutionContext.getMergedJobDataMap()獲取數據

1,設置數據:JobDetail和Trigger都可以調用usingJobData()方法設置數據。

@Configuration
@ConfigurationProperties("quartz")
public class QuartzConfig {
    @Bean
    public JobDetail quartzJob() {
        JobDataMap dataMap = new JobDataMap() {{
            put("job_str", "str_test");
        }};

        return JobBuilder.newJob(QuartzJob.class)
                .usingJobData(dataMap)
                .storeDurably()
                .build();
    }

    @Bean
    public Trigger quartzTrigger() {
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
        JobDataMap dataMap = new JobDataMap() {{
            put("trigger_int", 333);
        }};

        return TriggerBuilder.newTrigger()
                .forJob(quartzJob())
                .withSchedule(scheduleBuilder)
                .usingJobData(dataMap)
                .build();
    }

}

2,讀取數據:從JobExecutionContext中讀取JobDataMap獲取數據,執行業務邏輯。

public class QuartzJob extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        // get data from context
        JobDataMap dataMap = context.getMergedJobDataMap();
        for (Map.Entry<String, Object> data : dataMap.entrySet()) {
            System.out.printf("%s = %s\n", data.getKey(), data.getValue());
        }

        // do work
    }
}

3,運行輸出:

trigger_int = 333
job_str = str_test


通過SchedulerContext傳遞數據

另一個傳遞數據的方式是構建Scheduler時爲SchedulerContext設置數據。代碼詳見上文中第二、第三部分:

代碼下載:https://github.com/jextop/StarterApi/

src/main/java/com/starter/

├── controller/CheckController.java

├── job/QuartzJob.java

讀取數據方式一樣,因爲JobExecutionContext.getMergedJobDataMap()已經將數據合併到一起。也可以通過調用SchedulerContext獲取。


八,Spring框架Schedule功能的使用方

Spring Schedule支持異步執行定時任務,支持Cron表達式、固定間隔、固定頻率三種配置方式,能滿足小型項目對定時任務的功能需求。示例代碼:https://github.com/rickding/HelloJava/tree/master/HelloQuartz

src/main/java/com/hello/quartz/

├── QuartzApplication.java

├── ScheduledJob.java


代碼文件

功能要點

開啓功能

QuartzApplication.java

SpringApplication增加註解:

@EnableScheduling // 打開Schedule功能

@EnableAsysnc // 異步方式執行定時任務

配置任務

ScheduleJob.java

聲明定時任務,功能註解:

@Async // 異步方式執行,聲明在類上面時,作用於類裏面包含的任務

@Scheduled // 聲明定時任務執行時間

@EnableAsync

@EnableScheduling
@SpringBootApplication
public class QuartzApplication {
    public static void main(String[] args) {
        SpringApplication.run(QuartzApplication.class, args);
    }
}

1,cron表達式聲明定時任務,比如每10秒執行一次:

@Async
@Scheduled(cron = "0/10 * * * * ?")
public void scheduledCron() {
    System.out.printf("scheduled cron: %s\n", new Date());
}

2,固定頻率執行定時任務,比如每20秒執行一次:

@Scheduled(fixedRate = 1000 * 20, initialDelay = 1000 * 20)
public void scheduledRate() {
    System.out.printf("fixedRate: %s\n", new Date());
}

3,固定間隔執行定時任務,比如每20秒執行一次:

@Scheduled(fixedDelay = 1000 * 30, initialDelay = 1000 * 30)
public void scheduledDelay() {
    System.out.printf("fixedDelay: %s\n", new Date());
}

注意固定頻率和固定間隔兩類定時任務,默認在程序啓動時就會執行一次,可以通過指定initialDelay參數控制首次執行時間。

    scheduled cron: Sun Feb 09 18:09:19 CST 2020
    fixedRate: Sun Feb 09 18:09:28 CST 2020
    scheduled cron: Sun Feb 09 18:09:38 CST 2020

--------------------------------

如果您覺得這篇文章對您有幫助,請點個“贊”,博主感激不盡!

Jext技術社區專注領域:軟件工程實踐,JIRA研發管理分佈式系統架構,軟件質量保障

image.png

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章