quartz 实现动态修改 是需要配合数据库quartz持久化任务异常处理的吗(jobStoreTx)?TiggerKey有什么用?

总结日常点滴积累——ZengHao's Blog
Quartz任务调度(3)存储与持久化操作配置详细解析
内存存储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,
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,
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,
VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME)
其中各表的含义如下所示:
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
作为 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
org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
MS SQL Server
org.quartz.impl.jdbcjobstore.MSSQLDelegate
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
org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
如果我们的数据库平台没在上面列出,那么最好的选择就是,直接使用标准的 JDBC 代理 org.quartz.impl.jdbcjobstore.StdDriverDelegate 就能正常的工作。
以下是一些相关常用的配置属性及其说明:
org.quartz.jobStore.dataSource
用于 quartz.properties 中数据源的名称
org.quartz.jobStore.tablePrefix
指定用于 Scheduler 的一套数据库表名的前缀。假如有不同的前缀,Scheduler 就能在同一数据库中使用不同的表。
org.quartz.jobStore.userProperties
“use properties” 标记指示着持久性 JobStore 所有在 JobDataMap 中的值都是字符串,因此能以 名-值 对的形式存储,而不用让更复杂的对象以序列化的形式存入 BLOB 列中。这样会更方便,因为让你避免了发生于序列化你的非字符串的类到 BLOB 时的有关类版本的问题。
org.quartz.jobStore.misfireThreshold
在 Trigger 被认为是错过触发之前,Scheduler 还容许 Trigger 通过它的下次触发时间的毫秒数。默认值(假如你未在配置中存在这一属性条目) 是 60000(60 秒)。这个不仅限于 JDBC-JobStore;它也可作为 RAMJobStore 的参数
org.quartz.jobStore.isClustered
设置为 true 打开集群特性。如果你有多个 Quartz 实例在用同一套数据库时,这个属性就必须设置为 true。
org.quartz.jobStore.clusterCheckinInterval
设置一个频度(毫秒),用于实例报告给集群中的其他实例。这会影响到侦测失败实例的敏捷度。它只用于设置了 isClustered 为 true 的时候。
org.quartz.jobStore.maxMisfiresToHandleAtATime
这是 JobStore 能处理的错过触发的 Trigger 的最大数量。处理太多(超过两打) 很快会导致数据库表被锁定够长的时间,这样就妨碍了触发别的(还未错过触发) trigger 执行的性能。
org.quartz.jobStore.dontSetAutoCommitFalse
设置这个参数为 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
值为 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包中默认的配置文件
编写我们的测试文件,我们的测试环境是在quartz-2.2.2版本下进行的。下面的测试用例引用了上篇文章 ,关于Quartz的快速入门配置可移步参考这篇文章。
public class pickNewsJob implements Job {
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()
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
+————-+————–+—————+————–+—————–+—————–+
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();
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)));
if (tg instanceof SimpleTrigger
&& tg.getDescription().equals("jgroup1.DEFAULT")) {
scheduler.resumeJob(new JobKey(triggers.get(j),
triggerGroups.get(i)));
scheduler.start();
调用此方法,我们在数据库中异常中断任务记录就会被读取执行,然后被删除掉。
没有更多推荐了,spring 集成quartz 用数据库实现quartz的集群
1. 使用spring 支持quartz 的方式实现定时任务
2. 使用mysql做quartz的集群
二:准备工作
1. 安装mqyl(过程略,我的mysql ip为:192.168.75.128), 创建数据库quartz
2. 下载quartz相关包(http://www.quartz-scheduler.org/downloads/destination?name=quartz-1.8.6.tar.gz&bucket=tcdistributions&file=quartz-1.8.6.tar.gz),
解压后在 docs\dbTables 目录下有相关数据库的sql,用此sql脚本在quartz数据库中执行,创建表/索引(1.8.6和2.x sql脚本不同,这里使用的quartz是1.8.6,因为 spring3.x 不支持quartz 2.x 的版本)
三:demo实现
1. 使用maven管理依赖,依赖如下:
&properties&
&project.build.sourceEncoding&UTF-8&/project.build.sourceEncoding&
&spring.version&3.2.3.RELEASE&/spring.version&
&/properties&
&dependency&
&groupId&org.springframework&/groupId&
&artifactId&spring-context&/artifactId&
&version&${spring.version}&/version&
&/dependency&
&dependency&
&groupId&org.springframework&/groupId&
&artifactId&spring-context-support&/artifactId&
&version&${spring.version}&/version&
&/dependency&
&dependency&
&groupId&org.springframework&/groupId&
&artifactId&spring-expression&/artifactId&
&version&${spring.version}&/version&
&/dependency&
&dependency&
&groupId&org.springframework&/groupId&
&artifactId&spring-tx&/artifactId&
&version&${spring.version}&/version&
&/dependency&
&dependency&
&groupId&org.springframework&/groupId&
&artifactId&spring-test&/artifactId&
&version&${spring.version}&/version&
&scope&compile&/scope&
&/dependency&
&/dependencies&
2. applicationContext.xml 配置
&!-- 配置数据源 --&
&bean id="quartzDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"&
&property name="driverClassName"&
&value&org.gjt.mm.mysql.Driver&/value&
&/property&
&property name="url"&
&value&jdbc:mysql://192.168.75.128:3306/quartz?characterEncoding=utf8&/value&
&/property&
&property name="username"&
&value&root&/value&
&/property&
&property name="password"&
&value&123456&/value&
&/property&
&property name="maxActive" value="5"&&/property&
&property name="maxIdle" value="20"&&/property&
&property name="maxWait" value="50"&&/property&
&property name="defaultAutoCommit" value="true"&&/property&
&!-- 配置job --&
&bean id="jobHelloDetail" class="org.springframework.scheduling.quartz.JobDetailBean"&
&property name="jobClass" value="com.mas.db.JobHello"/& &!-- job实现类 --&
&!-- 配置trigger --&
&bean id="jobHelloTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"&
&property name="jobDetail" ref="jobHelloDetail" /& &!-- 对应的 job detail--&
&property name="cronExpression" value="1/2 * * * * ?" /& &!--cronExpression 表达式 --&
&bean name="quartzScheduler"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean"&
&property name="dataSource"&
&ref bean="quartzDataSource" /& &!--数据源引用指向,包含集群所需的所有表 --&
&/property&
&!--applicationContextSchedulerContextKey: 是org.springframework.scheduling.quartz.SchedulerFactoryBean这个类中把spring上下
文以key/value的方式存放在了quartz的上下文中了,可以用applicationContextSchedulerContextKey所定义的key得到对应的spring上下文, 可以看下源码注释--&
&property name="applicationContextSchedulerContextKey" value="applicationContextKey" /&
&property name="configLocation" value="classpath:quartz.properties" /& &!--用于指明quartz的配置文件的位置 --&
&!-- job trigger 实例加载到 scheduler factory中 --&
&property name="triggers"&
&ref bean="jobHelloTrigger" /&
&/property&
&/bean&3. quartz.properties 配置
#属性可为任何值,用在 JDBC JobStore 中来唯一标识实例,但是所有集群节点中必须相同。
org.quartz.scheduler.instanceName = HumsScheduler
#为 AUTO即可,基于主机名和时间戳来产生实例 ID。
org.quartz.scheduler.instanceId = AUTO
orgorg.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
#将任务持久化到数据中。因为集群中节点依赖于数据库来传播 Scheduler 实例的状态,你只能在使用 JDBC JobStore 时应用 Quartz 集群。这意味着你必须使用 JobStoreTX 或是 JobStoreCMT 作为 Job 存储;你不能在集群中使用 RAMJobStore。
orgorg.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
orgorg.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
#jobStore处理未按时触发的Job的数量
org.quartz.jobStore.maxMisfiresToHandleAtATime=10
#告诉了 Scheduler 实例要它参与到一个集群当中。这一属性会贯穿于调度框架的始终,用于修改集群环境中操作的默认行为。
org.quartz.jobStore.isClustered = true
#定义了Scheduler实例检入到数据库中的频率(单位:毫秒)。Scheduler 检查是否其他的实例到了它们应当检入的时候未检入;这能指出一个失败的 Scheduler 实例,且当前 Scheduler 会以此来接管任何执行失败并可恢复的 Job。通过检入操作,Scheduler 也会更新自身的状态记录。clusterChedkinInterval 越小,Scheduler 节点检查失败的 Scheduler 实例就越频繁。默认值是 15000 (即15 秒)。
org.quartz.jobStore.clusterCheckinInterval = 15000
com.mas.db.JobHello 的实现(注意job实现类的说明)
* 1. 继承 QuartzJobBean
* 2. 如果任务处理时间大于job触发频率时间,那么任务会并行处理,
* 实现 StatefulJob后job就会在一次任务没有处理完就不会进行下一次任务(任务的串行处理)
public class JobHello extends QuartzJobBean implements StatefulJob {
protected void executeInternal(JobExecutionContext arg0)
throws JobExecutionException {
//任务处理
System.out.println("job hello"+System.currentTimeMillis());
//模拟任务处理时间大于job触发频率
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
//任务处理 System.out.println("job hello"+System.currentTimeMillis()); //模拟任务处理时间大于job触发频率 Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } }}
5. 测试1. 可以起多个main方法进行测试(HelloTest 和 HelloTest2),多个main方法同时起动后只有一个job在执行,当停止一个main线程后,等到Scheduler实例检入到数据库中的频率(quartz.properties 是配置的org.quartz.jobStore.clusterCheckinInterval ) 过后,就会有一个线程接着执行这个jobpublic class HelloTest {
public static void main(String[] args) {
//定时任务开启模拟
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
}2.UpdateQuartzTest 测试类中是对 trigger的操作测试,如:修改触发频率、暂停、重启,获取trigger的运行状态,获取全部的trigger。如需更多对trigger或者job的操作可查看org.quartz.impl.StdScheduler
注意:修改trigger 的触发频率后,再暂停,然后重新启动;发现trigger是触发频率和初始化的时候相同, 说明:修改trigger触发频率只是对运行中的trigger有效,如果trigger重新启动则trigger触发频率还原为配置的频率
四:demo源码,在test中有两个测试类(HelloTest
和 HelloTest2 模拟部属两个集群。UpdateQuartzTest 中为操作trigger是测试,测试时需要启动HelloTest或者HelloTest2,两者都启动亦可).
完整代码:http://download.csdn.net/detail/convict_eva/9626160
六:Terracotta公司有个脱离关系型数据库实现集群解决方案的方法
备注:定时器时间配置
1.秒(0–59)
2.分钟(0–59)
3.小时(0–23)
4.月份中的日期(1–31)
5.月份(1–12或JAN–DEC)
6.星期中的日期(1–7或SUN–SAT)
7.年份()
"0/10 * * * * ?" 每10秒触发
"0 0 12 * * ?" 每天中午12点触发
"0 15 10 ? * *" 每天上午10:15触发
"0 15 10 * * ?" 每天上午10:15触发
"0 15 10 * * ? *" 每天上午10:15触发
"0 15 10 * * ? 年的每天上午10:15触发
"0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发
"0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发
"0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
"0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发
"0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发
"0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发
"0 15 10 15 * ?" 每月15日上午10:15触发
"0 15 10 L * ?" 每月最后一日的上午10:15触发
"0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6L " 2002年至2005年的每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发
星号(*):可用在所有字段中,表示对应时间域的每一个时刻,例如,*在分钟字段时,表示“每分钟”;
问号(?):该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于点位符;
减号(-):表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12;
逗号(,):表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五;
斜杠(/):x/y表达一个等步长序列,x为起始值,y为增量步长值。如在分钟字段中使用0/15,则表示为0,15,30和45秒,而5/15在分钟字段中表示5,20,35,50,你也可以使用*/y,它等同于0/y;
没有更多推荐了,

我要回帖

更多关于 quartz持久化任务异常处理 的文章

 

随机推荐