Quartz组件学习
一.引言
Quartz(中文:石英)是一个开源的任务调度框架。可以让你的程序在指定时间执行,也可以按照某一个频度执行。
支持数据库、监听器、插件、集群等特性。是一个专业的调度任务框架。
定时消息推送、定时抢购、定时发送邮件、定时统计等
java领域当中有三种技术选型,实现调度任务:
①jdk中util包下的Timer类(单线程)
②Spring Task 工具包,完成功能较为复杂的调度任务(集成方便)支持cron表达式
③Quartz 调度任务框架 功能非常强大,提供许多特性支持 支持cron表达式
二.第一个Demo示例
- 引入依赖
<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>
- 开发任务内容
public class MyJob implements Job {
/**
* 任务内容
* @param context 任务执行的上下文对象,可以获取调度任务的上下文信息
* (任务内容,触发时机等信息)
* @throws JobExecutionException
*/
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.err.println("当前系统时间是:"+new java.util.Date());
}
}
- 自定义调度任务
public class QuartzDemo {
public static void main(String[] args) throws SchedulerException {
//1.获取调度器对象(抛异常)
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//2.封装 任务内容对象(利用静态导入类,可用导入newJob静态方法)
//withIdentity给任务定义 名称 和 所属组
JobDetail jobDetail=newJob(MyJob.class).withIdentity("myJob","group1").build();
//3.封装 触发器对象
Trigger trigger=newTrigger().withIdentity("myTrigger","group1")
.startNow() //立即触发
.withSchedule(simpleSchedule() //触发规则
.repeatForever() //重复
.withIntervalInSeconds(6)) //6秒一触发
.build(); //构建
//4.任务调度(任务内容对象②,触发器对象③)
scheduler.scheduleJob(jobDetail,trigger);
//5.开启调度器
scheduler.start();
}
}
三.了解体系架构
- Job 任务内容接口
是一个接口,只定义一个方法execute(JobExecutionContext context),在实现接口的execute方法中编写所需要定时执行的Job(任务), JobExecutionContext类提供了调度应用的一些信息。Job运行时的信息保存在JobDataMap实例中。
ps:context多指上下文对象
-
JobDetail 任务详情对象
JobDetail 定义的是任务数据,而真正的执行逻辑是在Job中。sheduler每次执行,都会根据JobDetail创建一个新的Job实例。 -
Trigger 触发器对象
是一个类,描述触发Job执行的时间触发规则。主要有SimpleTrigger和CronTrigger这两个子类。当且仅当需调度一次或者以固定时间间隔周期执行调度,SimpleTrigger是最适合的选择;而CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度方案:如工作日周一到周五的15:00~16:00执行调度等
ps:Cron是Unix的时间表达式 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个周五
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 * * ?
在线Cron表达式生成器 http://cron.qqe2.com/
- Scheduler 调度器对象
代表一个Quartz的独立运行容器, Trigger 和JobDetail 可以注册到Scheduler中, 两者在Scheduler中拥有各自的组及名称, 组及名称是Scheduler查找定位容器中某一对象的依据, Trigger的组及名称必须唯一, JobDetail的组和名称也必须唯一(但可以和Trigger的组和名称相同,因为它们是不同类型的) 。
Scheduler定义了多个接口方法, 允许外部通过组及名称访问和控制容器中Trigger和JobDetail。
- JobBulider JobDetail的构建对象
用于定义/构建已经定义了Job实例的JobDetail实例
- TriggerBuilder Trigger的构建对象
用于定义/构建Trigger实例。
四.基本使用总结 & 体系架构总结
- 基本使用
- 任务内容 implements Job,在execute方法中任务详情
- 触发时机 trigger//简单 和 cron表达式(重要)
- 体系架构
- Trigger触发器
- Scheduler调度器 包含:调度方法和调度管理方法
五.Quartz调度任务框架和Springboot框架整合
六.SpringTask的使用
- 引入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- 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>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.9.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>6</source>
<target>6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 入口类
@EnableScheduling //开启spring Task的调度功能
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
- 任务类
@Component
public class MyJobSpringTask {
/**
* 调度方法:
*/
@Scheduled(cron ="* * * * * ?" )//spring Task只支持6位不带年的 cron表达式
public void s1(){
System.err.println("当前系统时间为:"+new java.util.Date());
}
}
七.与Spring集成
Maven坐标
<!-- 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>
定义任务内容
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());
}
}
配置文件
如:spring-quartz.xml
<?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="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>
测试
启动spring工厂测试
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class QuartzTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
}
}
八.存储方式
RAMJobStore和JDBCJobStore
类型 | 优点 | 缺点 |
---|---|---|
RAMJobStore(默认) | 不要外部数据库,配置容易,运行速度快 | 因为调度程序信息是存储在被分配给JVM的内存里面,所以,当应用程序停止运行时,所有调度信息将被丢失。另外因为存储到JVM内存里面,所以可以存储多少个Job和Trigger将会受到限制 |
JDBCJobStore | 支持集群,因为所有的任务信息都会保存到数据库中,可以控制事物,还有就是如果应用服务器关闭或者重启,任务信息都不会丢失,并且可以恢复因服务器关闭或者重启而导致执行失败的任务 | 运行速度的快慢取决与连接数据库的快慢 |
设置 JDBCJobStore
在应用程序中设置使用 JDBCJobStore
需要两步:首先必须创建作业仓库使用的数据库表。JDBCJobStore
与所有主流数据库都兼容,而且 Quartz 提供了一系列创建表的 SQL 脚本,能够简化设置过程。可以在 Quartz 发行包的 “docs/dbTables”目录中找到创建表的 SQL 脚本。第二,必须定义一些属性
-
创建Quartz数据库表
-
在 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
测试
九.集群支持
原理
虽然单个Quartz实例能给予你很好的Job调度能力,但它不能满足典型的企业需求,如可伸缩性、高可靠性满足。假如你需要故障转移的能力并能运行日益增多的 Job,Quartz集群势必成为你应用的一部分了。使用 Quartz 的集群能力可以更好的支持你的业务需求,并且即使是其中一台机器在最糟的时间崩溃了也能确保所有的 Job 得到执行。
一个 Quartz 集群中的每个节点是一个独立的 Quartz 应用,它又管理着其他的节点。意思是你必须对每个节点分别启动或停止。不像许多应用服务器的集群,独立的 Quartz 节点并不与另一其的节点或是管理节点通信。Quartz 应用是通过数据库表来感知到另一应用的。
图:表示了每个节点直接与数据库通信,若离开数据库将对其他节点一无所知
搭建步骤
-
准备配置文件
org.quartz.scheduler.instanceName: TestScheduler org.quartz.scheduler.instanceId: auto org.quartz.scheduler.skipUpdateCheck: true 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 org.quartz.jobStore.isClustered = true # 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
-
修改
spring-quartz.xml
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="configLocation" value="classpath:quartz.properties" /> <property name="triggers"> <list> <ref bean="trigger"></ref> <ref bean="trigger1"></ref> </list> </property> </bean>
-
启动spring工厂并测试