一概念介绍
默认
情况,Quartz的触发器,调度,任务等信息都是放在内存
中的,叫做RAMJobStore
。 好处是快速,坏处是一旦系统重启,那么信息就丢失了,就得全部从头来过。
所以Quartz还提供了另一个方式,可以把这些信息存放在数据库做,叫做 JobStoreTX
。 好处是就算系统重启了,目前运行到第几次了这些信息都是存放在数据库中的,那么就可以继续原来的步伐把计划任务无缝地继续做下去。 坏处就是性能上比内存慢一些,毕竟数据库读取总是要慢一些的
二 demo
代码其实和原来一样,只是多了些配置
建表sql
DROP DATABASE IF EXISTS quartz;
CREATE DATABASE quartz DEFAULT CHARACTER SET utf8;
USE quartz;
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(120) NOT NULL,
JOB_NAME VARCHAR(100) NOT NULL,
JOB_GROUP VARCHAR(100) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
JOB_CLASS_NAME VARCHAR(250) 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(120) 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(250) 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(120) 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(120) 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(120) NOT NULL,
TRIGGER_NAME VARCHAR(100) NOT NULL,
TRIGGER_GROUP VARCHAR(100) NOT NULL,
STR_PROP_1 VARCHAR(512) NULL,
STR_PROP_2 VARCHAR(512) NULL,
STR_PROP_3 VARCHAR(512) 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(120) 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(120) 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(120) NOT NULL,
TRIGGER_GROUP VARCHAR(100) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);
CREATE TABLE QRTZ_FIRED_TRIGGERS
(
SCHED_NAME VARCHAR(120) 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(120) 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(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);
commit;
配置文件
quartz.properties
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.scheduler.instanceName = MyScheduler
org.quartz.threadPool.threadCount = 3
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource = mysqlDatabase
org.quartz.dataSource.mysqlDatabase.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.mysqlDatabase.URL = jdbc:mysql://localhost:3306/quartz_test?characterEncoding=utf-8
org.quartz.dataSource.mysqlDatabase.user = root
org.quartz.dataSource.mysqlDatabase.password = 123456
org.quartz.dataSource.mysqlDatabase.maxConnections = 5
测试代码
总结:因为我怎么在配置文件中声明了存储方式,所以我们创建定时任务的信息都会存入数据库。而且每次创建时都会去数据库检查是否已经存在,已存在会抛出异常
Unable to store Job : 'mailgroup.mailjob1', because one already exists with this identification.
在这种情况下我们只需要调用scheduler.start();
启动即可
job类
package com.zyc;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class MailJob implements Job {
public void execute(JobExecutionContext context) throws JobExecutionException {
JobDetail detail = context.getJobDetail();
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
String now = sdf.format(new Date());
System.out.printf("发出了一封邮件, 当前时间是: %s%n" , now);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
测试类
package com.zyc;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;
import org.quartz.JobDetail;
import org.quartz.ObjectAlreadyExistsException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
public class TestQuartz {
public static void main(String[] args) throws Exception {
try {
assginNewJob();
} catch (ObjectAlreadyExistsException e) {
System.err.println("发现任务已经在数据库存在了,直接从数据库里运行:"+ e.getMessage());
// TODO Auto-generated catch block
resumeJobFromDatabase();
}
}
private static void resumeJobFromDatabase() throws Exception {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
// 等待200秒,让前面的任务都执行完了之后,再关闭调度器
Thread.sleep(200000);
scheduler.shutdown(true);
}
private static void assginNewJob() throws SchedulerException, InterruptedException {
// 创建调度器
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 定义一个触发器
Trigger trigger = newTrigger().withIdentity("trigger1", "group1") // 定义名称和所属的租
.startNow()
.withSchedule(simpleSchedule().withIntervalInSeconds(15) // 每隔15秒执行一次
.withRepeatCount(10)) // 总共执行11次(第一次执行不基数)
.build();
// 定义一个JobDetail
JobDetail job = newJob(MailJob.class) // 指定干活的类MailJob
.withIdentity("mailjob1", "mailgroup") // 定义任务名称和分组
.usingJobData("email", "admin@10086.com") // 定义属性
.build();
// 调度加入这个job
scheduler.scheduleJob(job, trigger);
// 启动
scheduler.start();
// 等待20秒,让前面的任务都执行完了之后,再关闭调度器
Thread.sleep(20000);
scheduler.shutdown(true);
}
}
三 再议任务持久化
quartz的持久化架构如下

RAMJobStore
这个就不多说了,以下是uartz的jar包中的默认配置文件
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
使用数据库存储数据
JobStoreTX和JobStoreCMT都是JobStoreSupport抽象类的实现类,JobStoreSupport
是基于JDBC实现了一些基本的功能的抽象类
。如果想要自己实现一套关于JDBC存储方式,那么可以继承此抽象类
。
JobStoreTX
此存储机制用于Quartz独立于应用容器
的事务管理
,如果是Tomcat容器管理的数据源,那我们定义的事物也不会传播给Quartz框架
内部。通俗的讲就是不管我们的Service服务本身业务代码是否执行成功,只要代码中调用了Quartz API的数据库操作,那任务状态就永久持久化了,就算业务代码抛出运行时异常任务状态也不会回滚到之前的状态。
用法
- 第一步配置org.quartz.jobStore.class属性:
org.quartz.jobStore.class= org.quartz.impl.jdbcjobstore.JobStoreTX
- 第二步配置驱动代理,以Mysql为例:
org.quartz.jobStore.driverDelegateClass= org.quartz.impl.jdbcjobstore.MSSQLDelegate
下面列出一个可用的数据库代理类表格,方便大家使用,如果表格中没有列出你想要的代理类,那就使用标准的 JDBC 代理:org.quartz.impl.jdbcjobstore.StdDriverDelegate

- 第三步配置数据源:
org.quartz.jobStore.dataSource=qzDS
org.quartz.dataSource.qzDS.driver= com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.URL= jdbc:mysql://localhost:3306/testDB
org.quartz.dataSource.qzDS.user= root
org.quartz.dataSource.qzDS.password= admin
org.quartz.dataSource.qzDS.maxConnection= 20
配置demo
org.quartz.scheduler.instanceName=DefaultQuartzScheduler
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=20
org.quartz.threadPool.threadPriority=5
org.quartz.jobStore.misfireThreshold=60000
org.quartz.jobStore.class= org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass= org.quartz.impl.jdbcjobstore.MSSQLDelegate
org.quartz.jobStore.tablePrefix=qrtz_
org.quartz.jobStore.dataSource=qzDS
org.quartz.dataSource.qzDS.driver= com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.URL= jdbc:mysql://localhost:3306/testDB
org.quartz.dataSource.qzDS.user= root
org.quartz.dataSource.qzDS.password= admin
org.quartz.dataSource.qzDS.maxConnection= 20
JobStoreCMT
CMT的全称是Container Managed Transactions
,表示容器管理事物,也就是让应用容器托管事物
。这里假设应用容器是Tomcat,并且项目和Quartz都是
使用Tomcat配置
的数据源,那么项目和Quartz的代码中就可以共用同一个事物,不管是业务代码还是Quartz内部抛出异常,Service服务内的所有数据操作都会回滚到原始状态。
配置步骤
- 第一步配置org.quartz.jobStore.class属性:
org.quartz.jobStore.class= org.quartz.impl.jdbcjobstore.JobStoreCMT
- 第二步配置驱动代理,以Mysql为例,其它代理类参考上面表格
org.quartz.jobStore.driverDelegateClass= org.quartz.impl.jdbcjobstore.MSSQLDelegate
- 第三步配置两个数据源
第一个:配置不受应用容器管理的数据源:
org.quartz.jobStore.nonManagedTXDataSource = qzDS
org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.URL = jdbc:mysql://localhost:3306/testDB
org.quartz.dataSource.qzDS.user = root
org.quartz.dataSource.qzDS.password = admin
org.quartz.dataSource.qzDS.maxConnections = 10
第二个:配置受应用容器管理的数据源:
org.quartz.dataSource.dataSource=myDS
org.quartz.dataSource.jndiURL = jdbc/mysql
org.quartz.dataSource.myDS.jndiAlwaysLookup = DB_JNDI_ALWAYS_LOOKUP
org.quartz.dataSource.myDS.java.naming.factory.initial = org.apache.naming.java.javaURLContextFactory
org.quartz.dataSource.myDS.java.naming.provider.url = http://localhost:8080
org.quartz.dataSource.myDS.java.naming.security.principal = root
org.quartz.dataSource.myDS.java.naming.security.credentials = admin
注意:
配置之前大家可能需要去了解学习一下JNDI+应用容器(Tomcat等)如何配置数据源
,本文就不讲述如何配置了。
网友评论