Quartz任務調度快速入門(3)

任務調度信息存儲

在默認情況下Quartz將任務調度的運行信息保存在內存中,這種方法提供了最佳的性能,因爲內存中數據訪問最快。不足之處是缺乏數據的持久性,當程序路途停止或系統崩潰時,所有運行的信息都會丟失。

比如我們希望安排一個執行100次的任務,如果執行到50次時系統崩潰了,系統重啓時任務的執行計數器將從0開始。在大多數實際的應用中,我們往往並不需要保存任務調度的現場數據,因爲很少需要規劃一個指定執行次數的任務。

對於僅執行一次的任務來說,其執行條件信息本身應該是已經持久化的業務數據(如鎖定到期解鎖任務,解鎖的時間應該是業務數據),當執行完成後,條件信息也會相應改變。當然調度現場信息不僅僅是記錄運行次數,還包括調度規則、JobDataMap中的數據等等。

如果確實需要持久化任務調度信息,Quartz允許你通過調整其屬性文件,將這些信息保存到數據庫中。使用數據庫保存任務調度信息後,即使系統崩潰後重新啓動,任務的調度信息將得到恢復。如前面所說的例子,執行50次崩潰後重新運行,計數器將從51開始計數。使用了數據庫保存信息的任務稱爲持久化任務。

 

通過配置文件調整任務調度信息的保存策略

其實Quartz JAR文件的org.quartz包下就包含了一個quartz.properties屬性配置文件並提供了默認設置。如果需要調整默認配置,可以在類路徑下建立一個新的quartz.properties,它將自動被Quartz加載並覆蓋默認的設置。

先來了解一下Quartz的默認屬性配置文件:

代碼清單5 quartz.properties:默認配置

①集羣的配置,這裏不使用集羣

org.quartz.scheduler.instanceName = DefaultQuartzScheduler

org.quartz.scheduler.rmi.export = false

org.quartz.scheduler.rmi.proxy = false

org.quartz.scheduler.wrapJobExecutionInUserTransaction = false

②配置調度器的線程池

org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool

org.quartz.threadPool.threadCount = 10

org.quartz.threadPool.threadPriority = 5

org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

③配置任務調度現場數據保存機制

org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

Quartz的屬性配置文件主要包括三方面的信息:

1)集羣信息;

2)調度器線程池;

3)任務調度現場數據的保存。

如果任務數目很大時,可以通過增大線程池的大小得到更好的性能。默認情況下,Quartz採用org.quartz.simpl.RAMJobStore保存任務的現場數據,顧名思義,信息保存在RAM內存中,我們可以通過以下設置將任務調度現場數據保存到數據庫中:

代碼清單6 quartz.properties:使用數據庫保存任務調度現場數據

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX

org.quartz.jobStore.tablePrefix = QRTZ_①數據表前綴

org.quartz.jobStore.dataSource = qzDS②數據源名稱

③定義數據源的具體屬性

org.quartz.dataSource.qzDS.driver = oracle.jdbc.driver.OracleDriver

org.quartz.dataSource.qzDS.URL = jdbc:oracle:thin:@localhost:1521:ora9i

org.quartz.dataSource.qzDS.user = stamen

org.quartz.dataSource.qzDS.password = abc

org.quartz.dataSource.qzDS.maxConnections = 10

要將任務調度數據保存到數據庫中,就必須使用org.quartz.impl.jdbcjobstore.JobStoreTX代替原來的org.quartz.simpl.RAMJobStore並提供相應的數據庫配置信息。首先①處指定了Quartz數據庫表的前綴,在②處定義了一個數據源,在③處具體定義這個數據源的連接信息。

你必須事先在相應的數據庫中創建Quartz的數據表(共8張),在Quartz的完整發布包的docs/dbTables目錄下擁有對應不同數據庫的SQL腳本。

 

查詢數據庫中的運行信息

任務的現場保存對於上層的Quartz程序來說是完全透明的,我們在src目錄下編寫一個如代碼清單6所示的quartz.properties文件後,重新運行代碼清單2或代碼清單3的程序,在數據庫表中將可以看到對應的持久化信息。當調度程序運行過程中途停止後,任務調度的現場數據將記錄在數據表中,在系統重啓時就可以在此基礎上繼續進行任務的調度。

代碼清單7 JDBCJobStoreRunner:從數據庫中恢復任務的調度

package com.baobaotao.basic.quartz;

import org.quartz.Scheduler;

import org.quartz.SchedulerFactory;

import org.quartz.SimpleTrigger;

import org.quartz.Trigger;

import org.quartz.impl.StdSchedulerFactory;

public class JDBCJobStoreRunner {

public static void main(String args[]) {

try {

SchedulerFactory schedulerFactory = new StdSchedulerFactory();

Scheduler scheduler = schedulerFactory.getScheduler();

①獲取調度器中所有的觸發器組

String[] triggerGroups = scheduler.getTriggerGroupNames();

②重新恢復在tgroup1組中,名爲trigger1_1觸發器的運行

for (int i = 0; i < triggerGroups.length; i++) {

String[] triggers = scheduler.getTriggerNames(triggerGroups[i]);

for (int j = 0; j < triggers.length; j++) {

Trigger tg = scheduler.getTrigger(triggers[j],triggerGroups[i]);

if (tg instanceof SimpleTrigger

&& tg.getFullName().equals("tgroup1.trigger1_1")) {②-1:根據名稱判斷

②-1:恢復運行

scheduler.rescheduleJob(triggers[j], triggerGroups[i],tg);

}

}

}

scheduler.start();

} catch (Exception e) {

e.printStackTrace();

}

}

}

當代碼清單2中的SimpleTriggerRunner執行到一段時間後非正常退出,我們就可以通過這個JDBCJobStoreRunner根據記錄在數據庫中的現場數據恢復任務的調度。Scheduler中的所有Trigger以及JobDetail的運行信息都會保存在數據庫中,這裏我們僅恢復tgroup1組中名稱爲trigger1_1的觸發器,這可以通過如②-1所示的代碼進行過濾,觸發器的採用GROUP.TRIGGER_NAME的全名格式。通過Scheduler#rescheduleJob(String triggerName,String groupName,Trigger newTrigger)即可重新調度關聯某個Trigger的任務。

 

下面我們來觀察一下不同時期qrtz_simple_triggers表的數據:

1.運行代碼清單2的SimpleTriggerRunner一小段時間後退出:


REPEAT_COUNT表示需要運行的總次數,而TIMES_TRIGGER表示已經運行的次數。

2.運行代碼清單7的JDBCJobStoreRunner恢復trigger1_1的觸發器,運行一段時間後退出,這時qrtz_simple_triggers中的數據如下:


首先Quartz會將原REPEAT_COUNT-TIMES_TRIGGER得到新的REPEAT_COUNT值,並記錄已經運行的次數(重新從0開始計算)。

3.重新啓動JDBCJobStoreRunner運行後,數據又將發生相應的變化:


4.繼續運行直至完成所有剩餘的次數,再次查詢qrtz_simple_triggers表:


這時,該表中的記錄已經變空。

值得注意的是,如果你使用JDBC保存任務調度數據時,當你運行代碼清單2的SimpleTriggerRunner然後退出,當再次希望運行SimpleTriggerRunner時,系統將拋出JobDetail重名的異常:

Unable to store Job with name: 'job1_1' and group: 'jGroup1', because one already exists with this identification.

因爲每次調用Scheduler#scheduleJob()時,Quartz都會將JobDetail和Trigger的信息保存到數據庫中,如果數據表中已經同名的JobDetail或Trigger,異常就產生了。

本文使用quartz 1.6版本,我們發現當後臺數據庫使用MySql時,數據保存不成功,該錯誤是Quartz的一個Bug,相信會在高版本中得到修復。因爲HSQLDB不支持SELECT * FROM TABLE_NAME FOR UPDATE的語法,所以不能使用HSQLDB數據庫。

小結

Quartz提供了最爲豐富的任務調度功能,不但可以制定週期性運行的任務調度方案,還可以讓你按照日曆相關的方式進行任務調度。Quartz框架的重要組件包括Job、JobDetail、Trigger、Scheduler以及輔助性的JobDataMap和SchedulerContext。

Quartz擁有一個線程池,通過線程池爲任務提供執行線程,你可以通過配置文件對線程池進行參數定製。Quartz的另一個重要功能是可將任務調度信息持久化到數據庫中,以便系統重啓時能夠恢復已經安排的任務。此外,Quartz還擁有完善的事件體系,允許你註冊各種事件的監聽器。

發佈了108 篇原創文章 · 獲贊 22 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章