當前位置:網站首頁>Quartz的持久化、集群使用實例(Quartz並發、Misfire、監聽器上手實例)

Quartz的持久化、集群使用實例(Quartz並發、Misfire、監聽器上手實例)

2022-01-27 20:12:04 程序員社區

Quartz的持久化、集群使用實例(Quartz並發、Misfire、監聽器上手實例) , 你了解嗎? 本文就為大家帶來了一篇 Quartz的持久化、集群使用實例(Quartz並發、Misfire、監聽器上手實例) 一起看看吧!
另外小編還收集了很多不錯的編程資源,希望對你有幫助:點擊查看
祝您生活愉快~

一、Quartz任務的持久化——JobStore

在前面的文章《Quartz並發、Misfire、監聽器上手實例》

好在Quartz還提供了基於數據庫存儲的QuartzJob存儲機制(JdbcJobStore),這樣就能解决如上Job存在內存中,一旦服務器關閉,無法補充執行的問題。

在開始之前,我們需要先引入Springboot中關於Quartz的啟動器,然後又因為要連接數據庫,所以必要的數據庫連接池及數據庫驅動也需要引入。

        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-quartz</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-jdbc</artifactId>        </dependency>        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>            <version>8.0.17</version>        </dependency>

然後。我們需要自己手動創建一個數據庫。我們可以在引入的Quartz的jar包中,找到自己數據庫類型的執行脚本,目錄為org/quartz/impl/jdbcjobstore/

比如我們的數據庫是postgresql,那麼就是用tables_postgres.sql中的內容。如果我們的數據庫是mysql的,那麼就使用tables_mysql_innodb.sql中的內容。

將如上的脚本在數據庫執行一下即可得到如下的十一張錶:

  • qrtz_blob_triggers,存儲Blob類型的Trigger,一般用於自定義觸發器;
  • qrtz_calendars,存儲Blob類型的Calendar信息;
  • qrtz_cron_triggers,存儲cron觸發器信息,包括cron錶達式和時區等信息;
  • qrtz_fired_triggers,存儲已經觸發的觸發器狀態信息和關聯的Job執行信息;
  • qrtz_job_details,存儲Job的詳細信息;
  • qrtz_locks,存儲程序鎖信息;
  • qrtz_paused_trigger_grps,存儲已經暫停的觸發器群組信息;
  • qrtz_scheduler_state,存儲有關調度器的狀態信息;
  • qrtz_simple_triggers,存儲simpleTrigger觸發器信息,包括重複次數,間隔時間等;
  • qrtz_simprop_triggers
  • qrtz_triggers,存儲已經配置的觸發器信息;

然後,我們需要設置Quartz的配置信息,此處直接在application.properties中配置如下:

## 數據源配置spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.url=jdbc:mysql://localhost:3306/mydb?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&useSSL=truespring.datasource.username=rootspring.datasource.password=root## Quartz配置# 啟動jdbcJobStore而非RamJobStorespring.quartz.job-store-type=jdbc# 調度器的配置spring.quartz.properties.org.quartz.scheduler.instanceName=MyJobSchedulerspring.quartz.properties.org.quartz.scheduler.instanceId=AUTO# 線程池的配置spring.quartz.properties.org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPoolspring.quartz.properties.org.quartz.threadPool.threadCount=20spring.quartz.properties.org.quartz.threadPool.threadPriority=5# misfire配置spring.quartz.properties.org.quartz.jobStore.misfireThreshold=60000# jobStore的其它配置spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTXspring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegatespring.quartz.properties.org.quartz.jobStore.tablePrefix=qrtz_spring.quartz.properties.org.quartz.jobStore.isClustered=false

然後我們創建一個最簡單的Job和其配置類如下:

@Slf4jpublic class HelloJob extends QuartzJobBean {    @Override    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {        log.info("{}開始執行時間是:{}", jobExecutionContext.getJobDetail().getKey().getName(), new Date());        try {            Thread.sleep(3000);        } catch (InterruptedException e) {            e.printStackTrace();        }        log.info("{}結束執行時間是:{}", jobExecutionContext.getJobDetail().getKey().getName(), new Date());    }}
@[email protected]("cron.properties")public class HelloJobConfig {    @Value("${helloJob.cron}")    private String helloJobCron;    // HelloJob的啟動配置信息    @Bean    public JobDetail helloJobDetail(){        return JobBuilder.newJob(HelloJob.class)                .withIdentity("helloJob","myJobGroup")                .storeDurably()                .build();    }    @Bean    public Trigger helloJobTrigger(){        return TriggerBuilder.newTrigger()                .forJob(helloJobDetail())                .withIdentity("helloJobTrigger","myTriggerGroup")                .startNow()                .withSchedule(CronScheduleBuilder.cronSchedule(helloJobCron))                .build();    }}

cronProperties配置文件內容如下:

helloJob.cron=0 10 21 * * ?

假設當前時間是21:09,那麼運營該程序後,等到21:10任務就能定時啟動了。

隨後,查看下數據庫的錶信息如下:

  • qrtz_job_detail記錄了系統中所有的job detail信息;

    Quartz的持久化、集群使用實例(Quartz並發、Misfire、監聽器上手實例)插圖
    qrtz_job_details
  • qrtz_cron_triggers記錄了所有的cron錶達式;

    Quartz的持久化、集群使用實例(Quartz並發、Misfire、監聽器上手實例)插圖1
    qrtz_cron_triggers
  • qrtz_fired_triggers記錄了所有的觸發記錄;

    Quartz的持久化、集群使用實例(Quartz並發、Misfire、監聽器上手實例)插圖2
    qrtz_fired_triggers

至此,我們完成了使用JobStore對JOB進行持久化的例子。

二、JOB持久化使得任務可以得到補執行

JOB的持久化究竟有什麼作用?能不能舉個例子看看?

在此,我們需要設計一個實驗例子,假設現在是21:09分,我們設置的Job運行時間時21:10分,按照正常情况來說,Job肯定會准時執行。但是如果突然服務器發生了某種意外,宕機了,等到21:15分才恢複正常運行,那麼我們遠程設置在21:10執行的JOB還能正常執行嗎?

我們還是使用上面的例子,假設此時沒有使用JdbcStore,而是使用的RAMStore,那麼,服務器在21:15分恢複正常運行時只會認為該JOB永遠沒有觸發的機會,並不會去補執行它。

如何切換為使用RAMStore?
將application.properties中所有關於quartz的配置都删除即可。

而如果我們使用的是JdbcStore,那麼就看首次將該JOB持久化時使用的misfire策略是什麼了。
我們在前面一篇文章中介紹過,CronTrigger有三種misfire策略:

  • withMisfireHandlingInstructionFireAndProceed,這是默認的策略,意思是,錯過多少次就不去補救了,現在立馬執行一次,後續則是按照原計劃執行。
  • withMisfireHandlingInstructionDoNothing,意思是,所有錯過的都不去補救了,直接按照原計劃執行。
  • withMisfireHandlingInstructionIgnoreMisfires,意思是,所有錯過的都需要按照順序一個個補執行。

在默認情况下,我們使用JdbcStore,並且不配置misfire策略,那麼就是默認使用的withMisfireHandlingInstructionFireAndProceed策略,在21:09分首次啟動系統,並將該Job持久化到數據庫中,然後21:10分將服務器故意關閉,等到21:15分才恢複啟動,結果原Job會在現在立即得到補執行。

當然,你可以設置成其它的misfire策略,再進行試驗,由於該試驗已經在上一篇關於Quartz的文章中詳細介紹過了,此處不再贅述。

如何更改JOB的misfire策略?
在JOB的配置類裏面對觸發器進行設置即可,如下所示。

    @Bean    public Trigger helloJobTrigger() {        return TriggerBuilder.newTrigger()                .forJob(helloJobDetail())                .withIdentity("helloJobTrigger", "myTriggerGroup")                .startNow()                .withSchedule(CronScheduleBuilder.cronSchedule(helloJobCron)                        .withMisfireHandlingInstructionDoNothing())                .build();    }

需要注意的是,如果我們更改Job的執行時間,只在cron.properties裏面修改是沒有效果的。因為默認情况下,使用JdbcStore的時候,JOB的cronTrigger和下次執行時間都已經保存在數據庫中了,下次啟動直接讀取數據庫信息就運行Job了。

如果此時我們要修改JOB,那麼就需要增加如下配置項:

# 是否覆蓋持久化的job信息#spring.quartz.overwrite-existing-jobs=true

如此,每次系統運行都會清空數據庫中的Job信息,重新進行初始化。但是這樣的話,Job的持久化也就沒有了補執行任務的意義。

三、Job持久化使得集群可以協作調度任務

倘若我們不使用JdbcStore,在多臺機器上同時啟動相同的程序,那麼所有的任務都會在所有機器上都執行,因為各個機器相互之間不能感知到對方,更無法知道某個JOB是否只允許被一臺機器執行。

如果我們在JOB上加上了@DisallowConcurrentExecution,那麼就可以保證多臺機器上JOB不會並發執行。

但是這也僅僅是保證不並發而已,談不上集群的協作。比如,某臺正在執行任務的機器宕機了,那麼未執行完的任務和還未執行的任務怎麼辦?在當前情况下,其它機器並不會接管宕機的機器,繼續執行任務。

此時我們需要更改配置文件中的使用集群:

spring.quartz.properties.org.quartz.jobStore.isClustered=true

如此,才能使得其它機器接替宕機的機器,繼續任務的執行。

版權聲明
本文為[程序員社區]所創,轉載請帶上原文鏈接,感謝
https://cht.chowdera.com/2022/01/202201272012037515.html

隨機推薦