Quartz学习笔记
文章目录
⼀、概述
Quartz是⼀个开放源码的任务调度框架。Quartz功能强⼤,可以让你的程序在指定时间执⾏,也可以按照某⼀个频度执⾏,⽀持数据库、监听器、插件、集群等特性。
使⽤场景:定时消息推送、定时抢购、定时发送邮件、定时统计等。
二、搭建简单的任务调度环境
1. 创建一个Maven项目,引入依赖jar
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.1</version>
</dependency>
2. 创建任务类,定义任务内容
package com.demo.job;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.util.Date;
public class MyJob implements Job {
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
//输出系统当前时间
System.out.println(new Date());
}
}
3. 构建调度任务功能类
package com.demo.test;
import com.demo.job.MyJob;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;
import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;
public class QuartzJobTest {
public static void main(String[] args) {
// 1. 获取调度器
Scheduler scheduler = null;
try {
scheduler = StdSchedulerFactory.getDefaultScheduler();
} catch (SchedulerException e) {
e.printStackTrace();
}
// 2. 包装任务内容
JobDetail jobDetail = newJob(MyJob.class)
.withIdentity("job1", "group1")
.build();
// 3. 定义触发器
CronTrigger trigger = newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(cronSchedule("* * * * * ? *"))
.build();
// 4. 组装任务
try {
scheduler.scheduleJob(jobDetail,trigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
// 5. 启动调度器,开始调度
try {
scheduler.start();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
三、Quartz体系架构
Job
是⼀个接⼝,只定义⼀个⽅法execute(JobExecutionContext context),在实现接⼝的execute
⽅法中编写所需要定时执⾏的Job(任务), JobExecutionContext类提供了调度应⽤的⼀些信息。
Job运⾏时的信息保存在JobDataMap实例中。
JobDetail
JobDetail 定义的是任务数据,⽽真正的执⾏逻辑是在Job中。sheduler每次执⾏,都会根据
JobDetail创建⼀个新的Job实例
Trigger
是⼀个类,描述触发Job执⾏的时间触发规则。主要有SimpleTrigger和CronTrigger这两个⼦类。
当且仅当需调度⼀次或者以固定时间间隔周期执⾏调度,SimpleTrigger是最适合的选择;
⽽CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度⽅案:如⼯作⽇周⼀到周
五的15:00~16:00执⾏调度等
Cron表达式的格式:秒 分 时 ⽇ ⽉ 周 年(可选)。
字段名 | 允许的值 | 允许的特殊字符 |
---|---|---|
秒 | 0-59 | , – * / |
分 | 0-59 | , – * / |
时 | 0-23 | , – * / |
⽇ | 1-31 | , – * ? / L W C |
⽉ | 1-12 or JAN-DEC | , – * / |
周 | 1-7 or SUN-SAT | , – * ? / L C # MON FRI |
年(可选字段) | empty, 1970-2099 | , – * / |
允许的特殊字符:
- “?”字符:表示不确定的值
- “,”字符:指定数个值
- “-”字符:指定⼀个值的范围
- “/”字符:指定⼀个值的增加幅度。n/m表示从n开始,每次增加m
- “L”字符:⽤在⽇表示⼀个⽉中的最后⼀天,⽤在周表示该⽉最后⼀个星期X
- “W”字符:指定离给定⽇期最近的⼯作⽇(周⼀到周五)
- “#”字符:表示该⽉第⼏个周X。6#3表示该⽉第3个周五(从周日开始,6代表周五)
Cron表达式范例:
- 每隔5秒执⾏⼀次:*/5 * * * * ?
- 每隔1分钟执⾏⼀次:0 */1 * * * ?
- 每天23点执⾏⼀次:0 0 23 * * ?
- 每天凌晨1点执⾏⼀次:0 0 1 * * ?
- 每⽉1号凌晨1点执⾏⼀次:0 0 1 1 * ?
- 每⽉最后⼀天23点执⾏⼀次:0 0 23 L * ?
- 每周星期天凌晨1点实⾏⼀次:0 0 1 ? * L
- 在26分、29分、33分执⾏⼀次:0 26,29,33 * * * ?
- 每天的0点、13点、18点、21点都执⾏⼀次:0 0 0,13,18,21 * * ?
Scheduler
代表⼀个Quartz的独⽴运⾏容器,Trigger和JobDetail可以注册到Scheduler中, 两者在Scheduler中拥有各⾃的组及名称, 组及名称是Scheduler查找定位容器中某⼀对象的依据,Trigger的组及名称必须唯⼀, JobDetail的组和名称也必须唯⼀(但可以和Trigger的组和名称相同,因为它们是不同类型的)。Scheduler定义了多个接⼝⽅法, 允许外部通过组及名称访问和控制容器中Trigger和JobDetail。
JobBulider
⽤于定义/构建已经定义了Job实例的JobDetail实例
TriggerBuilder
⽤于定义/构建Trigger实例。
四、Quartz与Spring集成
1. 创建Spring项目,引入依赖jar
<!-- quartz依赖 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.1</version>
</dependency>
<!--spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>3.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>3.2.8.RELEASE</version>
</dependency>
2. 创建任务类,定义任务内容
package com.demo.job;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.util.Date;
public class MyJob implements Job {
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
//输出系统当前时间
System.out.println(new Date());
}
}
3. 创建并编写spring的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 创建JobDetail -->
<bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<!-- 指定任务类 -->
<property name="jobClass" value="com.demo.job.MyJob"></property>
<!-- 当Job在没有可以使⽤的trigger的情况下 不删除 -->
<property name="durability" value="true"></property>
</bean>
<!-- 注意 spring quartz整合 ⼀个trigger只可以绑定⼀个JobDetail ⼀个jobDetail可以被多个Trigger所使⽤ -->
<bean id="trigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<!-- 绑定JobDetail -->
<property name="jobDetail" ref="jobDetail"></property>
<property name="cronExpression" value="0-30 * * * * ?"></property>
</bean>
<bean id="trigger1" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<!--绑定JobDetail-->
<property name="jobDetail" ref="jobDetail"></property>
<property name="cronExpression" value="45-55 * * * * ?"></property>
</bean>
<!--注册trigger,进行任务调度 -->
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="trigger"></ref>
<ref bean="trigger1"></ref>
</list>
</property>
</bean>
</beans>
五、Quartz与Spring Boot集成
1. 创建Spring Boot项目,引入依赖jar
<!-- quartz依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
2. 创建任务类,定义任务内容
package com.demo.job;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.util.Date;
public class MyJob implements Job {
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
//输出系统当前时间
System.out.println(new Date());
}
}
3. 创建controller
package com.demo.controller;
import com.demo.job.MyJob;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;
@RestController
public class QuartzController {
@Autowired
private Scheduler scheduler;
/**
* 新增调度任务的方法
* @return
*/
@RequestMapping("/add")
public String addJob(String jobName,String jobGroup,String triggerName,String triggerGroup,String cronExpression){
try {
//包装任务
JobDetail jobDetail = newJob(MyJob.class).withIdentity(jobName,jobGroup).build();
//创建触发器及触发规则
Trigger trigger = newTrigger()
//指定触发规则 ,cronExpression为Cron表达式
.withSchedule(cronSchedule(cronExpression))
//指定触发器名称及所属组
.withIdentity(triggerName,triggerGroup)
//创建触发器
.build();
//进行调度
scheduler.scheduleJob(jobDetail,trigger);
return "success";
} catch (SchedulerException e) {
e.printStackTrace();
}
return "fail";
}
/**
* 暂停调度任务
*/
@RequestMapping("/pause")
public String pause(String jobName,String jobGroup){
try {
scheduler.pauseJob(JobKey.jobKey(jobName,jobGroup));
return "success";
} catch (SchedulerException e) {
e.printStackTrace();
}
return "fail";
}
/**
* 开始调度任务
*/
@RequestMapping("/remuse")
public String remuse(String jobName,String jobGroup){
try {
scheduler.resumeJob(JobKey.jobKey(jobName,jobGroup));
return "success";
} catch (SchedulerException e) {
e.printStackTrace();
}
return "fail";
}
}
六、存储⽅式
1. RAMJobStore和JDBCJobStore存储方式对比
类型 | 优点 | 缺点 |
---|---|---|
RAMJobStore(默认) | 不要外部数据库,配置容易,运⾏速度快 | 因为调度程序信息是存储在被分配给JVM的内存⾥⾯,所以,当应⽤程序停⽌运⾏时,所有调度信息将被丢失。另外因为存储到JVM内存⾥⾯,所以可以存储多少个Job和Trigger将会受到限制 |
JDBCJobStore | ⽀持集群,因为所有的任务信息都会保存到数据库中,可以控制事物,还有就是如果应⽤服务器关闭或者重启,任务信息都不会丢失,并且可以恢复因服务器关闭或者重启⽽导致执⾏失败的任务 | 运⾏速度的快慢取决与连接数据库的快慢 |
2. 设置 JDBCJobStore
在应⽤程序中设置使⽤ JDBCJobStore
需要两步:
1. ⾸先必须创建作业仓库使⽤的数据库表。
JDBCJobStore 与所有主流数据库都兼容,⽽且 Quartz 提供了⼀系列创建表的 SQL 脚本,能够简化设置过程。可以在 Quartz 发⾏包的 “docs/dbTables”⽬录中找到创建表的 SQL 脚本,使用对应的SQL脚本创建表:
>Quartz 发⾏包下载地址:链接:https://pan.baidu.com/s/1CZ1khHlRc9SyIul10uDNDQ 提取码:agjx
2. 创建quartz.properties
配置文件,并定义⼀些属性
在 `quartz.properties`⽂件中指定 JDBCJobStore 属性
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
# Using RAMJobStore
## if using RAMJobStore, please be sure that you comment out the following
## - org.quartz.jobStore.tablePrefix,
## - org.quartz.jobStore.driverDelegateClass,
## - org.quartz.jobStore.dataSource
#org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
# Using JobStoreTX
## Be sure to run the appropriate script(under docs/dbTables) first to create tables
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
# Configuring JDBCJobStore with the Table Prefix
org.quartz.jobStore.tablePrefix = QRTZ_
# Using DriverDelegate
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# Using datasource
org.quartz.jobStore.dataSource = qzDS
# Define the datasource to use
org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.URL = jdbc:mysql://localhost:3306/quartz
org.quartz.dataSource.qzDS.user = root
org.quartz.dataSource.qzDS.password = root
org.quartz.dataSource.qzDS.maxConnections = 30
3. 定义spring的配置文件spring-quartz.xml
在进行任务调度时加入以下配置,指定jdbcJobStore的配置文件:
<property name="configLocation" value="classpath:quartz.properties"></property>
完整的spring配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 创建JobDetail -->
<bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<!-- 指定任务类 -->
<property name="jobClass" value="com.demo.job.MyJob"></property>
<!-- 当Job在没有可以使⽤的trigger的情况下 不删除 -->
<property name="durability" value="true"></property>
</bean>
<!-- 注意 spring quartz整合 ⼀个trigger只可以绑定⼀个JobDetail ⼀个jobDetail可以被多个Trigger所使⽤ -->
<bean id="trigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<!-- 绑定JobDetail -->
<property name="jobDetail" ref="jobDetail"></property>
<property name="cronExpression" value="0-30 * * * * ?"></property>
</bean>
<bean id="trigger1" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<!--绑定JobDetail-->
<property name="jobDetail" ref="jobDetail"></property>
<property name="cronExpression" value="45-55 * * * * ?"></property>
</bean>
<!--注册trigger ,进行任务调度 -->
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<!-- 指定jdbcJobStore使用的配置文件 -->
<property name="configLocation" value="classpath:quartz.properties"></property>
<property name="triggers">
<list>
<ref bean="trigger"></ref>
<ref bean="trigger1"></ref>
</list>
</property>
</bean>
</beans>
4. 运行测试
可以看到数据库中有任务存储进去。
六、集群⽀持
1. 原理
虽然单个Quartz实例能给予你很好的Job调度能⼒,但它不能满⾜典型的企业需求,如可伸缩性、⾼可靠性。假如你需要故障转移的能⼒并能运⾏⽇益增多的 Job,Quartz集群势必成为你应⽤的⼀部分了。使⽤ Quartz 的集群能⼒可以更好的⽀持你的业务需求,并且即使是其中⼀台机器在最糟的时间崩溃了也能确保所有的 Job 得到执⾏。
⼀个 Quartz 集群中的每个节点是⼀个独⽴的 Quartz 应⽤,它⼜管理着其他的节点。意思是你必须对每个节点分别启动或停⽌。不像许多应⽤服务器的集群,独⽴的 Quartz 节点并不与另⼀其他的节点通信或是管理节点通信。Quartz 应⽤是通过数据库表来感知到另⼀应⽤的。
图:表示了每个节点直接与数据库通信,若离开数据库将对其他节点⼀⽆所知
2. 搭建集群
1. 创建quartz-cluster.properties
配置文件
#==============================================================
#Configure Main Scheduler Properties
#==============================================================
org.quartz.scheduler.instanceName = quartzScheduler
org.quartz.scheduler.instanceId = AUTO
#==============================================================
#Configure JobStore
#==============================================================
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.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 3000
org.quartz.jobStore.dataSource = myDS
#==============================================================
#Configure DataSource
#==============================================================
org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=UTF-8
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = root
org.quartz.dataSource.myDS.maxConnections = 30
#==============================================================
#Configure ThreadPool
#==============================================================
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 5
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
2. 创建spring-quartz.xml
配置文件
在进行任务调度时加入以下配置,指定使用集群的配置文件:
<property name="configLocation" value="classpath:quartz-cluster.properties"></property>
完整的spring配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 创建JobDetail -->
<bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<!-- 指定任务类 -->
<property name="jobClass" value="com.demo.job.MyJob"></property>
<!-- 当Job在没有可以使⽤的trigger的情况下 不删除 -->
<property name="durability" value="true"></property>
</bean>
<!-- 注意 spring quartz整合 ⼀个trigger只可以绑定⼀个JobDetail ⼀个jobDetail可以被多个Trigger所使⽤ -->
<bean id="trigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<!-- 绑定JobDetail -->
<property name="jobDetail" ref="jobDetail"></property>
<property name="cronExpression" value="0-30 * * * * ?"></property>
</bean>
<bean id="trigger1" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<!--绑定JobDetail-->
<property name="jobDetail" ref="jobDetail"></property>
<property name="cronExpression" value="30-59 * * * * ?"></property>
</bean>
<!--注册trigger,进行任务管理-->
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<!-- 指定集群的配置文件 -->
<property name="configLocation" value="classpath:quartz-cluster.properties"></property>
<property name="triggers">
<list>
<ref bean="trigger"></ref>
<ref bean="trigger1"></ref>
</list>
</property>
</bean>
</beans>