SpringBoot 整合Quarz
1.数据库准备
Quartz 存储任务信息有两种方式,使用内存或者使用数据库来存储,这里我们采用 MySQL 数据库存储的方式,首先需要新建 Quartz 的相关表,sql 脚本下载地址:http://www.quartz-scheduler.org/downloads/,名称为 tables_mysql.sql,创建成功后数据库中多出 11 张表
2.数据库表介绍
表名 | 说明 |
---|---|
qrtz_blob_triggers | Trigger作为Blob类型存储(用于Quartz用户用JDBC创建他们自己定制的Trigger类型,JobStore 并不知道如何存储实例的时候) |
qrtz_calendars | 以Blob类型存储Quartz的Calendar日历信息, quartz可配置一个日历来指定一个时间范围 |
qrtz_cron_triggers | 存储Cron Trigger,包括Cron表达式和时区信息。 |
qrtz_fired_triggers | 存储与已触发的Trigger相关的状态信息,以及相联Job的执行信息 |
qrtz_job_details | 存储每一个已配置的Job的详细信息 |
qrtz_locks | 存储程序的非观锁的信息(假如使用了悲观锁) |
qrtz_paused_trigger_graps | 存储已暂停的Trigger组的信息 |
qrtzschedulerstate | 存储少量的有关 Scheduler的状态信息,和别的 Scheduler 实例(假如是用于一个集群中) |
qrtzsimpletriggers | 存储简单的 Trigger,包括重复次数,间隔,以及已触的次数 |
qrtz_triggers | 存储已配置的 Trigger的信息 |
qrztsimproptriggers |
3. Maven 主要依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<!--mysql 驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!--pagehelper分页-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
这里使用 druid
作为数据库连接池,Quartz
默认使用 c3p0
4.配置文件
spring:
datasource:
name: mysql_test
type: com.alibaba.druid.pool.DruidDataSource
#druid相关配置
druid:
#监控统计拦截的filters
filter: stat,config
# driver-class-name: com.mysql.cj.jdbc.Driver
#基本属性
url: jdbc:mysql://localhost:3306/mydb
username: root
password: 123456
#初始化连接数
initial-size: 10
#最小活跃连接数
min-size: 5
#最大活跃连接数
max-active: 20
#获取连接的等待时间
max-wait: 60000
#间隔多久进行一次检查,检查需要关闭的空闲连接
time-between-eviction-runs-millis: 60000
#一个连接在池中最小的生存时间(5分钟)
min-evictable-idle-time-millis: 300000
validation-query: SELECT 'X'
# 验证空闲的连接,若无法验证,则删除连接
test-while-idle: true
# 不检测池中连接的可用性(默认false)
# 导致的问题是,若项目作为服务端,数据库连接被关闭时,客户端调用就会出现大量的timeout
test-on-borrow: false
#在返回连接池之前是否验证对象
test-on-return: false
#打开PSCache,并指定每个连接上PSCache的大小。oracle设为true,mysql设为false。分库分表较多推荐设置为false
#第三发连接池在使用的时候,获取到Connection后,使用完毕,调用关闭方法,并没有将Connection关闭,只是放回到连接池中
#如果调用这个方法,而没有手动关闭PreparedStatement,就可能造成内存溢出,但是JDK1.7实现了AutoCloseable接口,就不需要关闭了
pool-prepared-statements: false
max-pool-prepared-statement-per-connection-size: 20
# connection-properties:
use-unfair-lock: true
quartz:
#相关属性配置
properties:
org:
quartz:
scheduler:
# 集群名,区分同一系统的不同实例,若使用集群功能,则每一个实例都要使用相同的名字
instanceName: clusteredScheduler
# 若是集群下,每个instanceId必须唯一
instanceId: AUTO
threadPool:
#一般使用这个便可
class: org.quartz.simpl.SimpleThreadPool
#线程数量,不会动态增加
threadCount: 10
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
jobStore:
#选择JDBC的存储方式
class: org.quartz.impl.jdbcjobstore.JobStoreTX
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
tablePrefix: QRTZ_
useProperties: false
isClustered: true
clusterCheckinInterval: 15000
job-store-type: jdbc
#是否等待任务执行完毕后,容器才会关闭
wait-for-jobs-to-complete-on-shutdown=false
#配置的job是否覆盖已经存在的JOB信息
overwrite-existing-jobs: false
1. 讲一下最后两个配置
- wait-for-jobs-to-complete-on-shutdown:Quartz默认false,是否等待任务运行完毕后关闭Spring容器,若是为false的情况下,可能出现**
java.lang.IllegalStateException: JobStore is shutdown - aborting retry
**异常,推荐开启。 - overwrite-existing-jobs:这个是配置文件的job是否会覆盖数据库正在运行的job。quartz启动之后,会以数据库的为准,若该属性为false,则配置文件修改后不会起作用。
5.Quartz的核心元素
Quartz调度依靠的三大核心元素就是:Scheduler、Trigger、Job。
1.job(任务)
作用:具体要执行的业务逻辑,比如:发送短信、发送邮件、访问数据库、同步数据等。2. 2.Trigger(触发器)
作用:用来定义Job(任务)触发条件、触发时间,触发间隔,终止时间等。
四大类型:SimpleTrigger、CornTrigger、DateIntervalTrigger、NthIncludedDayTrigger。
3. scheduler(调度器)
作用:Scheduler启动Trigger去执行Job。
类型:Scheduler由scheduler工厂创建:DirectSchedulerFactory 或者 StdSchedulerFactory。
第二种工厂StdSchedulerFactory使用较多,因为 DirectSchedulerFactory 使用起来不够方便,需要作许多详细的手工编码设置。
Scheduler 主要有三种:RemoteMBeanScheduler, RemoteScheduler 和 StdScheduler。
6. job
实现 job类 重写 execute方法
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class EquipmentJob implements Job {
public EquipmentJob() {
}
@Override
public void execute(JobExecutionContext jobExecutionContext) {
System.out.println("-----------定时更新设备工作时间开始------------");
EquipmentServer equipmentServer = SpringUtil.getBean(EquipmentServer.class);
if (!equipmentServer.workingHours()) {
equipmentServer.workingHours();
}
System.out.println("-----------定时更新设备工作时间结束------------");
}
}
注解说明
@PersistJobDataAfterExecution:告诉Quartz在成功执行了Job实现类的execute方法后(没有发生任何异常),更新JobDetail中JobDataMap的数据,使得该JobDetail实例在下一次执行的时候,JobDataMap中是更新后的数据,而不是更新前的旧数据
@DisallowConcurrentExecution:告诉Quartz不要并发地执行同一个JobDetail实例。
package com.spsw.erp.config;
import com.spsw.erp.job.*;
import com.spsw.erp.job.project.QualificationStartJob;
import com.spsw.erp.quartz.DynamicSchedulerFactory;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import javax.sql.DataSource;
@Configuration
public class SchedulerConfig {
//调度器
@Bean
public SchedulerFactoryBean getSchedulerFactoryBean(@Qualifier("dataSource") DataSource dataSource) {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setAutoStartup(true);
schedulerFactoryBean.setDataSource(dataSource);
schedulerFactoryBean.setTriggers(configTriggersList());
return schedulerFactoryBean;
}
@Bean
public DynamicSchedulerFactory dynamicSchedulerFactory(Scheduler scheduler) {
DynamicSchedulerFactory dynamicSchedulerFactory = new DynamicSchedulerFactory();
dynamicSchedulerFactory.setScheduler(scheduler);
return dynamicSchedulerFactory;
}
private Trigger[] configTriggersList() {
return new Trigger[]{
getQualificationJobDetailTrigger().getObject()
};
}
//gudin
@Bean
public JobDetailFactoryBean getQualificationJobDetail() {
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(QualificationStartJob.class);
factoryBean.setDurability(true);
return factoryBean;
}
// cron 触发器
@Bean
public CronTriggerFactoryBean getQualificationJobDetailTrigger() {
JobDetailFactoryBean jobDetailFactoryBean =getQualificationJobDetail();
CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
factoryBean.setJobDetail(jobDetailFactoryBean.getObject());
// String cron = "0 0 1 * * ?";//每天凌晨1点
String cron = "0 0/5 * * * ?";//每5分钟
// String cron = "0 0 */1 * * ?";//每个小时
// String cron = "*/5 * * * * ?";//每5秒
factoryBean.setCronExpression(cron);
factoryBean.setMisfireInstruction(2);
return factoryBean;
}
}