Spring Boot 中使用 Quartz(三)優化版
今天要跟大家接續上次Spring Boot 中使用 Quartz(二)動態設定 Scheduler這篇文章。
不知道大家有發現嗎?如果我們的排程是每 30 秒一次,程式在啟動的時候會花很多時間,主要應該是花在排程表的 delete 與 insert 上。
今天就是跟大家講解一下,我後來的解決方案。
Quick Start
再進行之前,我們先來觀察一下上次的這張表
| 1 | select * from qrtz_cron_triggers | 

我們可以發現,如果是 30 秒執行一次,我們只需要兩筆資料就可以了。
如:1
20 * * * * ?
30 * * * * ?
因此,我們將 SchedulerUtil.java 調整為:
另外,我將 JobBuilder 與 JobDetail 分開,因為我們可以在     JobBuilder 中添加我們想要的參數。
如:1
2jobBuilder.usingJobData(JOB_DATA_GAMETIME, (int) gameConfig.getGameTime())
          .usingJobData(JOB_DATA_GAMETIMEOFFSET, (int) gameConfig.getGameTimeOffset());
驗證
| 1 | select * from qrtz_cron_triggers | 

補充說明
1. 排程避免啟動時刪除 Job
因為我不希望在微服務的架構下,我們每次重新啟動服務的時候都會去刪除 Job,因此我在 JobData 中加入我需要比對的參數判斷是否有異動與是否需要刪除 Job1
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 misfireThreshold 的 default 值是 60000
Donate
謝謝您的支持與鼓勵