Quartz-任务调度信息持久化到DB中
文章目錄
- 概述
- 操作步驟
- 執行腳本建立對應的表
- quartz數據表解釋
- 配置quartz.properties
- 示例
- 總結
- 示例源碼
概述
在默認情況下,Quartz將任務調度的運行信息保存在內存中。 這種方法提供了最佳的性能,因為在內存中的數據訪問速度最快;不足之處就是卻反數據的持久性,當程序中途停止或者系統崩潰時,所有運行的信息都會丟失。
比如我們希望安排一個執行100次的任務,如果執行到50次時系統崩潰了,系統重啟時任務的執行計數器將從0開始。在大多數實際的應用中,我們往往并不需要保存任務調度的現場數據,因為很少需要規劃一個指定執行次數的任務。對于僅執行一次的任務來說,其執行條件信息本身應該是已經持久化的業務數據,當執行完成后,條件信息也會相應改變。當然調度現場信息不僅僅是記錄運行次數,還包括調度規則、JobDataMap中的數據等等。
如果確實需要持久化任務調度信息,Quartz允許你通過調整其屬性文件,將這些信息保存到數據庫中。使用數據庫保存任務調度信息后,即使系統崩潰后重新啟動,任務的調度信息將得到恢復。如前面所說的例子,執行50次崩潰后重新運行,計數器將從51開始計數。使用了數據庫保存信息的任務稱為持久化任務。
操作步驟
執行腳本建立對應的表
方式一: 從下載的壓縮包中找 quartz-2.2.3\docs\dbTables中
方式二:https://github.com/quartz-scheduler/quartz/tree/quartz-1.8.x/docs/dbTables
這里我們使用Oracle數據庫,我們執行tables_oracle.sql即可,共計11個表。
quartz數據表解釋
配置quartz.properties
首先,我們需要在我們的屬性文件中表明使用JobStoreTX:
org.quartz.jobStore.class = org.quartz.ompl.jdbcjobstore.JobStoreTX然后我們需要配置能理解不同數據庫系統中某一特定方言的驅動代理,選擇選擇對應數據庫的代理類。
這些代理類在org.quar.impl.jdbcjobstore package或者其子下。
包括
- DB2v6Delegate (for DB2 version 6 and earlier)
- HSQLDBDelegate (for HSQLDB)
- MSSQLDelegate (for Microsoft SQLServer)
- PostgreSQLDelegate (for PostgreSQL)
- WeblogicDelegate (for using JDBC drivers made by WebLogic)
- OracleDelegate (for using Oracle)等
然后確定應用程序需要哪種類型的事務。 如果不需要將調度命令(例如添加和刪除觸發器)綁定到其他事務,那么可以通過使用JobStoreTX作為JobStore來管理事務(這是最常見的選擇)。
如果需要Quartz與其他事務(即在J2EE應用程序服務器中)一起工作,那么您應該使用JobStoreCMT - 在這種情況下,Quartz將讓應用程序服務器容器管理事務。
最后設置一個DataSource,JDBCJobStore可以從中獲取與數據庫的連接。 DataSources在Quartz屬性中使用幾種不同的方法之一進行定義。
- 一種方法是讓Quartz創建和管理DataSource本身 - 通過提供數據庫的所有連接信息。
- 另一種方法是讓Quartz使用由Quartz正在運行的應用程序服務器管理的DataSource,通過提供JDBCJobStore DataSource的JNDI名稱
要使用JDBCJobStore(并假定使用的是StdSchedulerFactory),首先需要將Quartz配置的JobStore類屬性設置為org.quartz.impl.jdbcjobstore.JobStoreTX 或 org.quartz.impl.jdbcjobstore.JobStoreCMT
將org.quartz.jobStore.useProperties配置參數設置為“true”(默認為false),以表示JDBCJobStore將JobDataMaps中的所有值都作為字符串,因此可以作為名稱 - 值對存儲 而不是在BLOB列中以其序列化形式存儲更多復雜的對象。 從長遠來看,這是更安全的,因為避免了將非String類序列化為BLOB的類版本問題。
示例
配置文件:
#============================================================================ # Configure Main Scheduler Properties #============================================================================org.quartz.scheduler.instanceName: ArtisanScheduler org.quartz.scheduler.instanceId: AUTO org.quartz.scheduler.skipUpdateCheck: trueorg.quartz.scheduler.rmi.export: false org.quartz.scheduler.rmi.proxy: false org.quartz.scheduler.wrapJobExecutionInUserTransaction: false#============================================================================ # Configure ThreadPool #============================================================================org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount: 3 org.quartz.threadPool.threadPriority: 5 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true#============================================================================ # Configure JobStore # (choose the right driverDelegateClass, for oracle:OracleDelegate ,for mysql:StdJDBCDelegate) #============================================================================org.quartz.jobStore.misfireThreshold: 60000org.quartz.jobStore.class : org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass : org.quartz.impl.jdbcjobstore.oracle.OracleDelegate org.quartz.jobStore.dataSource : qzDS org.quartz.jobStore.tablePrefix : QRTZ_ org.quartz.jobStore.useProperties : true org.quartz.jobStore.isClustered: false#============================================================================ # Configure Datasource ORACLE #============================================================================org.quartz.dataSource.qzDS.connectionProvider.class : com.xgj.quartz.quartzItself.saveInfoInDB.MyPoolingconnectionProviderorg.quartz.dataSource.qzDS.driverClassName : oracle.jdbc.driver.OracleDriver org.quartz.dataSource.qzDS.url : jdbc:oracle:thin:@172.25.246.11:1521:testbed org.quartz.dataSource.qzDS.username : cc org.quartz.dataSource.qzDS.password : xxxxJob
package com.xgj.quartz.quartzItself.saveInfoInDB;import java.text.SimpleDateFormat; import java.util.Date;import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.JobKey;public class MyPersistenceJob implements Job {@Overridepublic void execute(JobExecutionContext context)throws JobExecutionException {JobKey jobKey = context.getJobDetail().getKey();System.out.println("\n任務key "+ jobKey+ "執行時間:"+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));}}數據源連接
package com.xgj.quartz.quartzItself.saveInfoInDB;import java.sql.Connection; import java.sql.SQLException;import org.apache.commons.dbcp.BasicDataSource; import org.quartz.utils.ConnectionProvider;/*** * * @ClassName: MyPoolingconnectionProvider* * @Description: MyPoolingconnectionProvider數據源連接和quartz.properties配置文件。* * 數據源是自己定義的類,實現了quartz自帶的ConnectionProvider類,如果不想使用它,* 你也可以選擇其他數據源,* 比如Tomcat的DataSource,Spring的SimpleDriverDataSource等,自行選擇.* * DBCP數據源連接池的屬性,這里僅僅使用了必須的配置,其他配置也顯式設置,也可使用默認值,根據需要執行調整。* * @author: Mr.Yang* * @date: 2017年10月10日 下午9:49:07*/ public class MyPoolingconnectionProvider implements ConnectionProvider {/*** 使用apache dbcp數據源連接池*/private BasicDataSource datasource = new BasicDataSource();private String driverClassName;private String url;private String username;private String password;@Overridepublic Connection getConnection() throws SQLException {System.out.println("getConnection");return datasource.getConnection();}@Overridepublic void shutdown() throws SQLException {System.out.println("connection pool shutdown");datasource.close();}@Overridepublic void initialize() throws SQLException {try {System.out.println("inti connection");datasource.setDriverClassName(driverClassName);datasource.setUrl(url);datasource.setUsername(username);datasource.setPassword(password);} catch (Exception e) {e.printStackTrace();}}public void setDatasource(BasicDataSource datasource) {this.datasource = datasource;}public void setDriverClassName(String driverClassName) {this.driverClassName = driverClassName;}public void setUrl(String url) {this.url = url;}public void setUsername(String username) {this.username = username;}public void setPassword(String password) {this.password = password;}}測試類
package com.xgj.quartz.quartzItself.saveInfoInDB;import java.text.SimpleDateFormat; import java.util.Date;import org.quartz.DateBuilder; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerFactory; import org.quartz.SchedulerMetaData; import org.quartz.TriggerBuilder; import org.quartz.impl.StdSchedulerFactory; import org.quartz.impl.triggers.SimpleTriggerImpl;public class QuartzPersistenceTest {public static void main(String[] args) {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");try {System.out.println("------- 初始化 ----------------------");// 通過調度器工廠獲取調度器,初始化工程時須指定其使用我們自己的配置文件SchedulerFactory sf = new StdSchedulerFactory("quartz/quartz.properties");Scheduler sched = sf.getScheduler();// clear一下,因為使用數據庫儲存方式時,shutdown的時候沒有清除,第二次運行會報Job is already// existsched.clear();System.out.println("------- 初始化完成 -----------");// 下一分鐘開始執行Date runTime = DateBuilder.evenMinuteDate(new Date());System.out.println("------- Scheduling Job -------------------");// 任務詳情JobDetail job = JobBuilder.newJob(MyPersistenceJob.class).withIdentity("artisanJob", "artisanGroup").build();// 觸發器 重復20+1次 間隔2秒SimpleTriggerImpl trigger = (SimpleTriggerImpl) TriggerBuilder.newTrigger().withIdentity("artisanTrigger", "artisanGroup").startAt(runTime).build();trigger.setRepeatCount(20);trigger.setRepeatInterval(2000);System.out.println("------- 當前時間:" + sdf.format(new Date())+ " -----------------");// 調度器、觸發器、任務,三者關聯sched.scheduleJob(job, trigger);System.out.println(job.getKey() + " 開始job運行時間:"+ sdf.format(runTime));// 調度啟動sched.start();System.out.println("------- 開始調度器 Scheduler -----------------");System.out.println("------- 等待10秒-------------");try {Thread.sleep(1 * 10000L);} catch (Exception e) {}System.out.println("------- 關閉調度器 模擬異常退出---------------------");sched.shutdown(true);System.out.println("------- 異常退出 -----------------");SchedulerMetaData metaData = sched.getMetaData();System.out.println("目前執行了 "+ metaData.getNumberOfJobsExecuted() + " 個 jobs.");} catch (Exception e) {e.printStackTrace();}}}通過關閉quartz模擬異常中斷的場景
第一次運行日志:
------- 初始化 ---------------------- inti connection INFO StdSchedulerFactory - Using default implementation for ThreadExecutor INFO SimpleThreadPool - Job execution threads will use class loader of thread: main INFO SchedulerSignalerImpl - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl INFO QuartzScheduler - Quartz Scheduler v.2.2.3 created. INFO JobStoreTX - Using thread monitor-based data access locking (synchronization). INFO JobStoreTX - JobStoreTX initialized. INFO QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v2.2.3) 'ArtisanScheduler' with instanceId 'NON_CLUSTERED'Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.NOT STARTED.Currently in standby mode.Number of jobs executed: 0Using 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 StdSchedulerFactory - Quartz scheduler 'ArtisanScheduler' initialized from the specified file : 'quartz/quartz.properties' from the class resource path. INFO StdSchedulerFactory - Quartz scheduler version: 2.2.3 getConnection ------- 初始化完成 ----------- ------- Scheduling Job ------------------- ------- 當前時間:2017-10-12 17:35:53 ----------------- getConnection artisanGroup.artisanJob 開始job運行時間:2017-10-12 17:36:00 getConnection INFO JobStoreTX - Freed 0 triggers from 'acquired' / 'blocked' state. INFO JobStoreTX - Recovering 0 jobs that were in-progress at the time of the last shut-down. INFO JobStoreTX - Recovery complete. INFO JobStoreTX - Removed 0 'complete' triggers. INFO JobStoreTX - Removed 0 stale fired job entries. INFO QuartzScheduler - Scheduler ArtisanScheduler_$_NON_CLUSTERED started. getConnection ------- 開始調度器 Scheduler ----------------- ------- 等待10秒------------- getConnection getConnection getConnection任務key artisanGroup.artisanJob執行時間:2017-10-12 17:36:00 getConnection getConnection getConnection任務key artisanGroup.artisanJob執行時間:2017-10-12 17:36:02 getConnection ------- 關閉調度器 模擬異常退出--------------------- INFO QuartzScheduler - Scheduler ArtisanScheduler_$_NON_CLUSTERED shutting down. INFO QuartzScheduler - Scheduler ArtisanScheduler_$_NON_CLUSTERED paused. connection pool shutdown INFO QuartzScheduler - Scheduler ArtisanScheduler_$_NON_CLUSTERED shutdown complete. ------- 異常退出 ----------------- 目前執行了 2 個 jobs.數據庫表qrtz_simple_triggers數據:
將測試類中的下列代碼屏蔽,使其正常運行
System.out.println("------- 等待10秒-------------");try {Thread.sleep(1 * 10000L);} catch (Exception e) {}System.out.println("------- 關閉調度器 模擬異常退出---------------------");sched.shutdown(true);System.out.println("------- 異常退出 -----------------");再次運行
qrtz_simple_triggers 中的 times_triggered字段重0開始計算, 執行完成后,該表的數據為空。
總結
簡單的10個字概括就是:未執行,插入; 執行過,刪除。
示例源碼
代碼已托管到Github—> https://github.com/yangshangwei/SpringMaster
總結
以上是生活随笔為你收集整理的Quartz-任务调度信息持久化到DB中的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Quartz-SimpleTrigger
- 下一篇: Quartz-Java Web项目中使用