Quartz任务调度(3)存储与持久化操作配置详细解析

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: <div class="markdown_views"><h1 id="内存存储ramjobstore">内存存储RAMJobStore</h1><p>Quartz默认使用RAMJobStore,它的优点是速度。因为所有的 Scheduler 信息都保存在计算机内存中,访问这些数据随着电脑而变快。而无须访问数据库或IO等操作,但它的缺点是将 Job 和 Trigger 信

内存存储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

  1. 首先,我们需要在我们的属性文件中表明使用JobStoreTX:
    org.quartz.jobStore.class = org.quartz.ompl.jdbcjobstore.JobStoreTX
  2. 然后我们需要配置能理解不同数据库系统中某一特定方言的驱动代理:
数据库平台 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 就能正常的工作。

  1. 以下是一些相关常用的配置属性及其说明:
属性 默认值 描述
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();
}

调用此方法,我们在数据库中异常中断任务记录就会被读取执行,然后被删除掉。

目录
相关文章
|
12天前
|
数据采集 消息中间件 监控
Flume数据采集系统设计与配置实战:面试经验与必备知识点解析
【4月更文挑战第9天】本文深入探讨Apache Flume的数据采集系统设计,涵盖Flume Agent、Source、Channel、Sink的核心概念及其配置实战。通过实例展示了文件日志收集、网络数据接收、命令行实时数据捕获等场景。此外,还讨论了Flume与同类工具的对比、实际项目挑战及解决方案,以及未来发展趋势。提供配置示例帮助理解Flume在数据集成、日志收集中的应用,为面试准备提供扎实的理论与实践支持。
24 1
|
15天前
|
存储 缓存 NoSQL
深入解析Redis:一种快速、高效的键值存储系统
**Redis** 是一款高性能的键值存储系统,以其内存数据、高效数据结构、持久化机制和丰富的功能在现代应用中占有一席之地。支持字符串、哈希、列表、集合和有序集合等多种数据结构,适用于缓存、计数、分布式锁和消息队列等场景。安装Redis涉及下载、编译和配置`redis.conf`。基本操作包括键值对的设置与获取,以及哈希、列表、集合和有序集合的操作。高级特性涵盖发布/订阅、事务处理和Lua脚本。优化策略包括选择合适数据结构、配置缓存和使用Pipeline。注意安全、监控和备份策略,以确保系统稳定和数据安全。
62 1
|
29天前
|
机器学习/深度学习 存储 PyTorch
Pytorch中in-place操作相关错误解析及detach()方法说明
Pytorch中in-place操作相关错误解析及detach()方法说明
41 0
|
25天前
|
存储 NoSQL 算法
【Redis技术进阶之路】「底层源码解析」揭秘高效存储模型与数据结构底层实现(字典)(二)
【Redis技术进阶之路】「底层源码解析」揭秘高效存储模型与数据结构底层实现(字典)
36 0
|
1天前
|
JavaScript IDE 编译器
TypeScript中模块路径解析与配置:深入剖析与最佳实践
【4月更文挑战第23天】本文深入探讨了TypeScript中模块路径解析的原理与配置优化,包括相对路径、Node.js模块解析和路径别名。通过配置`baseUrl`、`paths`、`rootDirs`以及避免裸模块名,可以提升开发效率和代码质量。建议使用路径别名增强代码可读性,保持路径结构一致性,并利用IDE插件辅助开发。正确配置能有效降低维护成本,构建高效可维护的代码库。
|
1天前
|
JSON Java Maven
Javaweb之SpringBootWeb案例之自动配置以及常见方案的详细解析
Javaweb之SpringBootWeb案例之自动配置以及常见方案的详细解析
6 0
Javaweb之SpringBootWeb案例之自动配置以及常见方案的详细解析
|
1天前
|
SQL Java 数据库连接
Javaweb之Mybatis的基础操作之新增和更新操作的详细解析
Javaweb之Mybatis的基础操作之新增和更新操作的详细解析
8 0
|
1天前
|
SQL 存储 关系型数据库
数据库开发之图形化工具以及表操作的详细解析
数据库开发之图形化工具以及表操作的详细解析
13 0
|
1天前
|
JavaScript 前端开发 UED
深入解析JavaScript原生操作DOM技术
【4月更文挑战第22天】本文深入探讨JavaScript原生DOM操作技术,包括使用`getElement*`方法和CSS选择器获取元素,借助`createElement`与`appendChild`动态创建及插入元素,修改元素内容、属性和样式,以及删除元素。通过掌握这些技术,开发者能实现页面动态交互,但应注意避免过度操作DOM以优化性能和用户体验。
|
6天前
|
域名解析 网络协议 Linux
TCP/IP协议及配置、IP地址、子网掩码、网关地址、DNS与DHCP介绍
TCP/IP协议及配置、IP地址、子网掩码、网关地址、DNS与DHCP介绍

推荐镜像

更多