Spring Boot 中使用 Quartz(三)優化版

今天要跟大家接續上次Spring Boot 中使用 Quartz(二)動態設定 Scheduler這篇文章。

不知道大家有發現嗎?如果我們的排程是每 30 秒一次,程式在啟動的時候會花很多時間,主要應該是花在排程表的 deleteinsert 上。

今天就是跟大家講解一下,我後來的解決方案。

Quick Start

再進行之前,我們先來觀察一下上次的這張表

1
select * from qrtz_cron_triggers

我們可以發現,如果是 30 秒執行一次,我們只需要兩筆資料就可以了。
如:

1
2
0 * * * * ?
30 * * * * ?

因此,我們將 SchedulerUtil.java 調整為:

另外,我將 JobBuilderJobDetail 分開,因為我們可以在 JobBuilder 中添加我們想要的參數。
如:

1
2
jobBuilder.usingJobData(JOB_DATA_GAMETIME, (int) gameConfig.getGameTime())
.usingJobData(JOB_DATA_GAMETIMEOFFSET, (int) gameConfig.getGameTimeOffset());

驗證

1
select * from qrtz_cron_triggers

補充說明

1. 排程避免啟動時刪除 Job

因為我不希望在微服務的架構下,我們每次重新啟動服務的時候都會去刪除 Job,因此我在 JobData 中加入我需要比對的參數判斷是否有異動與是否需要刪除 Job

1
2
3
4
5
6
7
8
9
10
11
12
13
// 判斷是否有需要更新資料庫的排程
JobDetail oldJobDetail = scheduler.getJobDetail(jobDetail.getKey());
if(oldJobDetail!=null &&
gameConfig.getGameTime().toString().equals(oldJobDetail.getJobDataMap().get(JOB_DATA_GAMETIME).toString()) &&
gameConfig.getGameTimeOffset().toString().equals(oldJobDetail.getJobDataMap().get(JOB_DATA_GAMETIMEOFFSET).toString())) {
break;
} else {
log.info("刪除 scheduler.jobGroup : " + gameConfig.getId());
// 刪除舊的job
for (JobKey key : scheduler.getJobKeys(GroupMatcher.jobGroupEquals(String.valueOf(gameConfig.getId())))) {
scheduler.deleteJob(key);
}
}

完整的程式請參考:

2. 時間過了就不想要在補啟動

由於 Quartz 有防火措施,當一個正常的 Job 錯過時間了,會進入防火措施的設定,你可以設定你的防火措施時間要多長與錯過了需要如何補救。

因為我是不想要有任何防火措施,所以我調整了以下:

  • 設定防火時間
    application.yml

    1
    2
    3
    4
    5
    6
    7
    spring:
    quartz:
    properties:
    org:
    quartz:
    jobStore:
    misfireThreshold: 500
  • 建立 CronTriger 時加上 withMisfireHandlingInstructionDoNothing,表示錯過了不做任何事。

    1
    2
    3
    4
    Trigger trigger = TriggerBuilder.newTrigger().forJob(jobDetail)
    .withIdentity(jobDetail.getKey().getName(), jobDetail.getKey().getGroup())
    .withSchedule(CronScheduleBuilder.cronSchedule(cronExp).withMisfireHandlingInstructionDoNothing())
    .build();

3. 科普

3.1 這邊幫大家科普一下 CronTriger 的三種狀態,分別是

  • withMisfireHandlingInstructionDoNothing :
    不觸發執行,等待下次Cron觸發頻率到達時刻開始按照Cron頻率依次執行

  • withMisfireHandlingInstructionIgnoreMisfires :
    以錯過的第一個頻率時間立刻開始執行,重做錯過的所有頻率週期後,當下一次觸發頻率發生時間大於當前時間後,再按照正常的Cron頻率依次執行

  • withMisfireHandlingInstructionFireAndProceed :
    以當前時間為觸發頻率立刻觸發一次執行,然後按照Cron頻率依次執行

3.2 misfireThresholddefault 值是 60000

謝謝您的支持與鼓勵

Ads