內存存儲RAMJobStore
Quartz默認使用RAMJobStore,它的優點是速度。因為所有的 Scheduler 信息都保存在計算機內存中,訪問這些數據隨着電腦而變快。而無須訪問數據庫或IO等操作,但它的缺點是將 Job 和 Trigger 信息存儲在內存中的。因而我們每次重啟程序,Scheduler 的狀態,包括 Job 和 Trigger 信息都丟失了。
Quartz 的內存 Job 存儲的能力是由一個叫做 org.quartz.simple.RAMJobStore 類提供。在我們的quartz-2.x.x.jar包下的org.quartz包下即存儲了我們的默認配置quartz.properties。打開這個配置文件,我們會看到如下信息
# Default Properties file for use by StdSchedulerFactory # to create a Quartz Scheduler Instance, if a different # properties file is not explicitly specified. # 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.misfireThreshold: 60000 org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore #這里默認使用RAMJobStore
持久性JobStore
Quartz 提供了兩種類型的持久性 JobStore,為JobStoreTX和JobStoreCMT,其中:
1. JobStoreTX為獨立環境中的持久性存儲,它設計為用於獨立環境中。這里的 “獨立”,我們是指這樣一個環境,在其中不存在與應用容器的事物集成。這里並不意味着你不能在一個容器中使用 JobStoreTX,只不過,它不是設計來讓它的事特受容器管理。區別就在於 Quartz 的事物是否要參與到容器的事物中去。
2. JobStoreCMT 為程序容器中的持久性存儲,它設計為當你想要程序容器來為你的 JobStore 管理事物時,並且那些事物要參與到容器管理的事物邊界時使用。它的名字明顯是來源於容器管理的事物(Container Managed Transactions (CMT))。
持久化配置步驟
要將JobDetail等信息持久化我們的數據庫中,我們可按一下步驟操作:
1. 配置數據庫
在 /docs/dbTables 目錄下存放了幾乎所有數據庫的的SQL腳本,這里的 是解壓 Quartz 分發包后的目錄。我們使用常用mysql數據庫,下面是示例sql腳本代碼
# # Quartz seems to work best with the driver mm.mysql-2.0.7-bin.jar # # PLEASE consider using mysql with innodb tables to avoid locking issues # # In your Quartz properties file, you'll need to set # org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate # DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS; DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS; DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE; DROP TABLE IF EXISTS QRTZ_LOCKS; DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS; DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS; DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS; DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS; DROP TABLE IF EXISTS QRTZ_TRIGGERS; DROP TABLE IF EXISTS QRTZ_JOB_DETAILS; DROP TABLE IF EXISTS QRTZ_CALENDARS; CREATE TABLE QRTZ_JOB_DETAILS ( SCHED_NAME VARCHAR(80) NOT NULL, JOB_NAME VARCHAR(100) NOT NULL, JOB_GROUP VARCHAR(100) NOT NULL, DESCRIPTION VARCHAR(100) NULL, JOB_CLASS_NAME VARCHAR(100) NOT NULL, IS_DURABLE VARCHAR(1) NOT NULL, IS_NONCONCURRENT VARCHAR(1) NOT NULL, IS_UPDATE_DATA VARCHAR(1) NOT NULL, REQUESTS_RECOVERY VARCHAR(1) NOT NULL, JOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) ); CREATE TABLE QRTZ_TRIGGERS ( SCHED_NAME VARCHAR(80) NOT NULL, TRIGGER_NAME VARCHAR(100) NOT NULL, TRIGGER_GROUP VARCHAR(100) NOT NULL, JOB_NAME VARCHAR(100) NOT NULL, JOB_GROUP VARCHAR(100) NOT NULL, DESCRIPTION VARCHAR(100) NULL, NEXT_FIRE_TIME BIGINT(13) NULL, PREV_FIRE_TIME BIGINT(13) NULL, PRIORITY INTEGER NULL, TRIGGER_STATE VARCHAR(16) NOT NULL, TRIGGER_TYPE VARCHAR(8) NOT NULL, START_TIME BIGINT(13) NOT NULL, END_TIME BIGINT(13) NULL, CALENDAR_NAME VARCHAR(100) NULL, MISFIRE_INSTR SMALLINT(2) NULL, JOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP) ); CREATE TABLE QRTZ_SIMPLE_TRIGGERS ( SCHED_NAME VARCHAR(80) NOT NULL, TRIGGER_NAME VARCHAR(100) NOT NULL, TRIGGER_GROUP VARCHAR(100) NOT NULL, REPEAT_COUNT BIGINT(7) NOT NULL, REPEAT_INTERVAL BIGINT(12) NOT NULL, TIMES_TRIGGERED BIGINT(10) NOT NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_CRON_TRIGGERS ( SCHED_NAME VARCHAR(80) NOT NULL, TRIGGER_NAME VARCHAR(100) NOT NULL, TRIGGER_GROUP VARCHAR(100) NOT NULL, CRON_EXPRESSION VARCHAR(100) NOT NULL, TIME_ZONE_ID VARCHAR(80), PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_SIMPROP_TRIGGERS ( SCHED_NAME VARCHAR(80) NOT NULL, TRIGGER_NAME VARCHAR(100) NOT NULL, TRIGGER_GROUP VARCHAR(100) NOT NULL, STR_PROP_1 VARCHAR(120) NULL, STR_PROP_2 VARCHAR(120) NULL, STR_PROP_3 VARCHAR(120) NULL, INT_PROP_1 INT NULL, INT_PROP_2 INT NULL, LONG_PROP_1 BIGINT NULL, LONG_PROP_2 BIGINT NULL, DEC_PROP_1 NUMERIC(13,4) NULL, DEC_PROP_2 NUMERIC(13,4) NULL, BOOL_PROP_1 VARCHAR(1) NULL, BOOL_PROP_2 VARCHAR(1) NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_BLOB_TRIGGERS ( SCHED_NAME VARCHAR(80) NOT NULL, TRIGGER_NAME VARCHAR(100) NOT NULL, TRIGGER_GROUP VARCHAR(100) NOT NULL, BLOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_CALENDARS ( SCHED_NAME VARCHAR(80) NOT NULL, CALENDAR_NAME VARCHAR(100) NOT NULL, CALENDAR BLOB NOT NULL, PRIMARY KEY (SCHED_NAME,CALENDAR_NAME) ); CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS ( SCHED_NAME VARCHAR(80) NOT NULL, TRIGGER_GROUP VARCHAR(100) NOT NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_FIRED_TRIGGERS ( SCHED_NAME VARCHAR(80) NOT NULL, ENTRY_ID VARCHAR(95) NOT NULL, TRIGGER_NAME VARCHAR(100) NOT NULL, TRIGGER_GROUP VARCHAR(100) NOT NULL, INSTANCE_NAME VARCHAR(100) NOT NULL, FIRED_TIME BIGINT(13) NOT NULL, SCHED_TIME BIGINT(13) NOT NULL, PRIORITY INTEGER NOT NULL, STATE VARCHAR(16) NOT NULL, JOB_NAME VARCHAR(100) NULL, JOB_GROUP VARCHAR(100) NULL, IS_NONCONCURRENT VARCHAR(1) NULL, REQUESTS_RECOVERY VARCHAR(1) NULL, PRIMARY KEY (SCHED_NAME,ENTRY_ID) ); CREATE TABLE QRTZ_SCHEDULER_STATE ( SCHED_NAME VARCHAR(80) NOT NULL, INSTANCE_NAME VARCHAR(100) NOT NULL, LAST_CHECKIN_TIME BIGINT(13) NOT NULL, CHECKIN_INTERVAL BIGINT(13) NOT NULL, PRIMARY KEY (SCHED_NAME,INSTANCE_NAME) ); CREATE TABLE QRTZ_LOCKS ( SCHED_NAME VARCHAR(80) NOT NULL, LOCK_NAME VARCHAR(40) NOT NULL, PRIMARY KEY (SCHED_NAME,LOCK_NAME) ); commit;
其中各表的含義如下所示:
表名 | 描述 |
---|---|
QRTZ_CALENDARS | 以 Blob 類型存儲 Quartz 的 Calendar 信息 |
QRTZ_CRON_TRIGGERS | 存儲 Cron Trigger,包括 Cron 表達式和時區信息 |
QRTZ_FIRED_TRIGGERS | 存儲與已觸發的 Trigger 相關的狀態信息,以及相聯 Job 的執行信息 |
QRTZ_PAUSED_TRIGGER_GRPS | 存儲已暫停的 Trigger 組的信息 |
QRTZ_SCHEDULER_STATE | 存儲少量的有關 Scheduler 的狀態信息,和別的 Scheduler 實例(假如是用於一個集群中) |
QRTZ_LOCKS | 存儲程序的非觀鎖的信息(假如使用了悲觀鎖) |
QRTZ_JOB_DETAILS | 存儲每一個已配置的 Job 的詳細信息 |
QRTZ_JOB_LISTENERS | 存儲有關已配置的 JobListener 的信息 |
QRTZ_SIMPLE_TRIGGERS | 存儲簡單的 Trigger,包括重復次數,間隔,以及已觸的次數 |
QRTZ_BLOG_TRIGGERS Trigger | 作為 Blob 類型存儲(用於 Quartz 用戶用 JDBC 創建他們自己定制的 Trigger 類型,JobStore 並不知道如何存儲實例的時候) |
QRTZ_TRIGGER_LISTENERS | 存儲已配置的 TriggerListener 的信息 |
QRTZ_TRIGGERS | 存儲已配置的 Trigger 的信息 |
2. 使用JobStoreTX
- 首先,我們需要在我們的屬性文件中表明使用JobStoreTX:
org.quartz.jobStore.class = org.quartz.ompl.jdbcjobstore.JobStoreTX - 然后我們需要配置能理解不同數據庫系統中某一特定方言的驅動代理:
數據庫平台 | Quartz 代理類 |
---|---|
Cloudscape/Derby | org.quartz.impl.jdbcjobstore.CloudscapeDelegate |
DB2 (version 6.x) | org.quartz.impl.jdbcjobstore.DB2v6Delegate |
DB2 (version 7.x) | org.quartz.impl.jdbcjobstore.DB2v7Delegate |
DB2 (version 8.x) | org.quartz.impl.jdbcjobstore.DB2v8Delegate |
HSQLDB | org.quartz.impl.jdbcjobstore.PostgreSQLDelegate |
MS SQL Server | org.quartz.impl.jdbcjobstore.MSSQLDelegate |
Pointbase | org.quartz.impl.jdbcjobstore.PointbaseDelegate |
PostgreSQL | org.quartz.impl.jdbcjobstore.PostgreSQLDelegate |
(WebLogic JDBC Driver) | org.quartz.impl.jdbcjobstore.WebLogicDelegate |
(WebLogic 8.1 with Oracle) | org.quartz.impl.jdbcjobstore.oracle.weblogic.WebLogicOracleDelegate |
Oracle | org.quartz.impl.jdbcjobstore.oracle.OracleDelegate |
如果我們的數據庫平台沒在上面列出,那么最好的選擇就是,直接使用標准的 JDBC 代理 org.quartz.impl.jdbcjobstore.StdDriverDelegate 就能正常的工作。
- 以下是一些相關常用的配置屬性及其說明:
屬性 | 默認值 | 描述 |
---|---|---|
org.quartz.jobStore.dataSource | 無 | 用於 quartz.properties 中數據源的名稱 |
org.quartz.jobStore.tablePrefix | QRTZ_ | 指定用於 Scheduler 的一套數據庫表名的前綴。假如有不同的前綴,Scheduler 就能在同一數據庫中使用不同的表。 |
org.quartz.jobStore.userProperties | False | “use properties” 標記指示着持久性 JobStore 所有在 JobDataMap 中的值都是字符串,因此能以 名-值 對的形式存儲,而不用讓更復雜的對象以序列化的形式存入 BLOB 列中。這樣會更方便,因為讓你避免了發生於序列化你的非字符串的類到 BLOB 時的有關類版本的問題。 |
org.quartz.jobStore.misfireThreshold | 60000 | 在 Trigger 被認為是錯過觸發之前,Scheduler 還容許 Trigger 通過它的下次觸發時間的毫秒數。默認值(假如你未在配置中存在這一屬性條目) 是 60000(60 秒)。這個不僅限於 JDBC-JobStore;它也可作為 RAMJobStore 的參數 |
org.quartz.jobStore.isClustered | False | 設置為 true 打開集群特性。如果你有多個 Quartz 實例在用同一套數據庫時,這個屬性就必須設置為 true。 |
org.quartz.jobStore.clusterCheckinInterval | 15000 | 設置一個頻度(毫秒),用於實例報告給集群中的其他實例。這會影響到偵測失敗實例的敏捷度。它只用於設置了 isClustered 為 true 的時候。 |
org.quartz.jobStore.maxMisfiresToHandleAtATime | 20 | 這是 JobStore 能處理的錯過觸發的 Trigger 的最大數量。處理太多(超過兩打) 很快會導致數據庫表被鎖定夠長的時間,這樣就妨礙了觸發別的(還未錯過觸發) trigger 執行的性能。 |
org.quartz.jobStore.dontSetAutoCommitFalse | False | 設置這個參數為 true 會告訴 Quartz 從數據源獲取的連接后不要調用它的 setAutoCommit(false) 方法。這在少些情況下是有幫助的,比如假如你有這樣一個驅動,它會抱怨本來就是關閉的又來調用這個方法。這個屬性默認值是 false,因為大多數的驅動都要求調用 setAutoCommit(false)。 |
org.quartz.jobStore.selectWithLockSQL | SELECT * FROM {0}LOCKS WHERE LOCK_NAME = ? FOR UPDATE | 這必須是一個從 LOCKS 表查詢一行並對這行記錄加鎖的 SQL 語句。假如未設置,默認值就是 SELECT * FROM {0}LOCKS WHERE LOCK_NAME = ? FOR UPDATE,這能在大部分數據庫上工作。{0} 會在運行期間被前面你配置的 TABLE_PREFIX 所替換。 |
org.quartz.jobStore.txIsolationLevelSerializable | False | 值為 true 時告知 Quartz(當使用 JobStoreTX 或 CMT) 調用 JDBC 連接的 setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE) 方法。這有助於阻止某些數據庫在高負載和長時間事物時鎖的超時。 |
4. 我們還需要配置Datasource 屬性
屬性 | 必須 | 說明 |
---|---|---|
org.quartz.dataSource.NAME.driver | 是 | JDBC 驅動類的全限名 |
org.quartz.dataSource.NAME.URL | 是 | 連接到你的數據庫的 URL(主機,端口等) |
org.quartz.dataSource.NAME.user | 否 | 用於連接你的數據庫的用戶名 |
org.quartz.dataSource.NAME.password | 否 | 用於連接你的數據庫的密碼 |
org.quartz.dataSource.NAME.maxConnections | 否 | DataSource 在連接接中創建的最大連接數 |
org.quartz.dataSource.NAME.validationQuary | 否 | 一個可選的 SQL 查詢字串,DataSource 用它來偵測並替換失敗/斷開的連接。例如,Oracle 用戶可選用 select table_name from user_tables,這個查詢應當永遠不會失敗,除非直的就是連接不上了。 |
下面是我們的一個quartz.properties屬性文件配置實例:
org.quartz.scheduler.instanceName = MyScheduler
org.quartz.threadPool.threadCount = 3
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.dataSource = myDS
org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/quartz?characterEncoding=utf-8
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = root
org.quartz.dataSource.myDS.maxConnections =5
配置好quartz.properties屬性文件后,我們只要**將它放在類路徑下,然后運行我們的程序,即可覆蓋在quartz.jar包中默認的配置文件
3. 測試
編寫我們的測試文件,我們的測試環境是在quartz-2.2.2版本下進行的。下面的測試用例引用了上篇文章 ,關於Quartz的快速入門配置可移步參考這篇文章。
public class pickNewsJob implements Job { @Override public void execute(JobExecutionContext jec) throws JobExecutionException { SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); System.out.println("在"+sdf.format(new Date())+"扒取新聞"); } public static void main(String args[]) throws SchedulerException { JobDetail jobDetail = JobBuilder.newJob(pickNewsJob.class) .withIdentity("job1", "jgroup1").build(); SimpleTrigger simpleTrigger = TriggerBuilder .newTrigger() .withIdentity("trigger1") .withSchedule(SimpleScheduleBuilder.repeatSecondlyForTotalCount(10, 2)) .startNow() .build(); //創建scheduler SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); scheduler.scheduleJob(jobDetail, simpleTrigger); scheduler.start(); } }
執行測試方法,能看到控制台打印如下日志信息,關注紅色部分,更注意其中的粗體部分,是我們quartz調用數據庫的一些信息:
INFO : org.quartz.core.SchedulerSignalerImpl - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
INFO : org.quartz.core.QuartzScheduler - Quartz Scheduler v.2.2.2 created.
INFO : org.quartz.impl.jdbcjobstore.JobStoreTX - Using thread monitor-based data access locking (synchronization).
INFO : org.quartz.impl.jdbcjobstore.JobStoreTX - JobStoreTX initialized.
INFO : org.quartz.core.QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v2.2.2) ‘MyScheduler’ with instanceId ‘NON_CLUSTERED’
Scheduler class: ‘org.quartz.core.QuartzScheduler’ - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool ‘org.quartz.simpl.SimpleThreadPool’ - with 3 threads.
Using job-store ‘org.quartz.impl.jdbcjobstore.JobStoreTX’ - which supports persistence. and is not clustered.
INFO : org.quartz.impl.StdSchedulerFactory - Quartz scheduler ‘MyScheduler’ initialized from default resource file in Quartz package: ‘quartz.properties’
INFO : org.quartz.impl.StdSchedulerFactory - Quartz scheduler version: 2.2.2
INFO : com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource - Initializing c3p0 pool… com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> z8kfsx9f1dp34iubvoy4d|7662953a, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> z8kfsx9f1dp34iubvoy4d|7662953a, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/quartz?characterEncoding=utf-8, lastAcquisitionFailureDefaultUser -> null, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 5, maxStatements -> 0, maxStatementsPerConnection -> 120, minPoolSize -> 1, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}
, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
INFO : org.quartz.impl.jdbcjobstore.JobStoreTX - Freed 0 triggers from ‘acquired’ / ‘blocked’ state.
INFO : org.quartz.impl.jdbcjobstore.JobStoreTX - Recovering 0 jobs that were in-progress at the time of the last shut-down.這里代表在我們任務開始時,先從數據庫查詢舊記錄,這些舊記錄是之前由於程序中斷等原因未能正常執行的,於是先Recovery回來並執行
INFO : org.quartz.impl.jdbcjobstore.JobStoreTX - Recovery complete.
INFO : org.quartz.impl.jdbcjobstore.JobStoreTX - Removed 0 ‘complete’ triggers.
INFO : org.quartz.impl.jdbcjobstore.JobStoreTX - Removed 0 stale fired job entries.
INFO : org.quartz.core.QuartzScheduler - Scheduler MyScheduler_$_NON_CLUSTERED started.
在21:28:12扒取新聞
在21:28:13扒取新聞
在21:28:15扒取新聞
在21:28:17扒取新聞
….
4. 拓展測試
我們再次運行測試方法,然后馬上中斷程序,查詢我們數據庫,會看到如下內容:
SELECT * FROM QRTZ_SIMPLE_TRIGGERS;
+————-+————–+—————+————–+—————–+—————–+
| SCHED_NAME | TRIGGER_NAME | TRIGGER_GROUP | REPEAT_COUNT | REPEAT_INTERVAL | TIMES_TRIGGERED |
+————-+————–+—————+————–+—————–+—————–+
| MyScheduler | trigger1 | DEFAULT | 9 | 2000 | 1 |
+————-+————–+—————+————–+—————–+—————–+
1 row in set (0.00 sec)
然后我們再運行程序,發現報錯了。
org.quartz.ObjectAlreadyExistsException: Unable to store Job : ‘jgroup1.job1’, because one already exists with this identification.
一般的,在我們的任務調度前,會先將相關的任務持久化到數據庫中,然后調用完在刪除記錄,這里在程序開始試圖將任務信息持久化到數據庫時,顯然和(因為我們之前中斷操作導致)數據庫中存在的記錄起了沖突。
5. 恢復異常中斷的任務
這個時候,我們可以選擇修改我們的job名和組名和triiger名,然后再運行我們的程序。查看控制台打印的信息部分展示如下:
INFO : org.quartz.impl.jdbcjobstore.JobStoreTX - Freed 1 triggers from ‘acquired’ / ‘blocked’ state.
INFO : org.quartz.impl.jdbcjobstore.JobStoreTX - Handling 1 trigger(s) that missed their scheduled fire-time.這里我們開始處理上一次異常未完成的存儲在數據庫中的任務記錄
INFO : org.quartz.impl.jdbcjobstore.JobStoreTX - Recovering 0 jobs that were in-progress at the time of the last shut-down.
INFO : org.quartz.impl.jdbcjobstore.JobStoreTX - Recovery complete.
INFO : org.quartz.impl.jdbcjobstore.JobStoreTX - Removed 0 ‘complete’ triggers.
INFO : org.quartz.impl.jdbcjobstore.JobStoreTX - Removed 1 stale fired job entries.
INFO : org.quartz.core.QuartzScheduler - Scheduler MyScheduler_$_NON_CLUSTERED started.
在21:42:13扒取新聞
在21:42:13扒取新聞
在21:42:14扒取新聞
在21:42:15扒取新聞
在21:42:16扒取新聞
在21:42:17扒取新聞
在21:42:18扒取新聞
在21:42:19扒取新聞
在21:42:20扒取新聞
在21:42:21扒取新聞
在21:42:22扒取新聞
在21:42:23扒取新聞
在21:42:24扒取新聞
在21:42:25扒取新聞
在21:42:26扒取新聞
在21:42:27扒取新聞
在21:42:28扒取新聞
在21:42:29扒取新聞
在21:42:30扒取新聞
我們會發現,“扒取新聞”一句的信息打印次數超過十次,但我們在任務調度中設置了打印十次,說明它恢復了上次的任務調度。
而如果我們不想執行新的任務,只想純粹地恢復之前異常中斷任務,我們可以采用如下方法:
SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); // ①獲取調度器中所有的觸發器組 List<String> triggerGroups = scheduler.getTriggerGroupNames(); // ②重新恢復在tgroup1組中,名為trigger1觸發器的運行 for (int i = 0; i < triggerGroups.size(); i++) {//這里使用了兩次遍歷,針對每一組觸發器里的每一個觸發器名,和每一個觸發組名進行逐次匹配 List<String> triggers = scheduler.getTriggerGroupNames(); for (int j = 0; j < triggers.size(); j++) { Trigger tg = scheduler.getTrigger(new TriggerKey(triggers .get(j), triggerGroups.get(i))); // ②-1:根據名稱判斷 if (tg instanceof SimpleTrigger && tg.getDescription().equals("jgroup1.DEFAULT")) {//由於我們之前測試沒有設置觸發器所在組,所以默認為DEFAULT // ②-1:恢復運行 scheduler.resumeJob(new JobKey(triggers.get(j), triggerGroups.get(i))); } } } scheduler.start(); }
調用此方法,我們在數據庫中異常中斷任務記錄就會被讀取執行,然后被刪除掉。