SpringBoot组件系列01-任务调度Quartz
1.Quartz简介
Quartz一般用于项目中的定时任务,有些业务需求需要指定时间去执行,例如:我有个需求需要每天03:00去备份前一天重要的数据;还有我间隔10分钟需要定时扫描一次数据;还有我需要判断订单下单之后半个小时之内是否支付,没支付需要将订单状态改为超时未支付,并修改商品库存等业务。还有非常多的应用场景,这些都可以用Quartz来完成。
2.maven需要的包
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
3.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(200) not null,
job_group varchar(200) 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(200) not null,
trigger_group varchar(200) not null,
job_name varchar(200) not null,
job_group varchar(200) 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(200) 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(200) not null,
trigger_group varchar(200) 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(200) not null,
trigger_group varchar(200) not null,
cron_expression varchar(200) 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(200) not null,
trigger_group varchar(200) 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(200) not null,
trigger_group varchar(200) 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(200) 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(200) 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(200) not null,
trigger_group varchar(200) not null,
instance_name varchar(200) 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(200) null,
job_group varchar(200) 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(200) 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;
4.Quartz需要用到的配置(quartz.properties)
#quartz集群配置
# ===========================================================================
# Configure Main Scheduler Properties 调度器属性
# ===========================================================================
#调度标识名 集群中每一个实例都必须使用相同的名称
org.quartz.scheduler.instanceName=DefaultQuartzScheduler
#ID设置为自动获取 每一个必须不同
org.quartz.scheduler.instanceid=AUTO
#============================================================================
# Configure ThreadPool
#============================================================================
#线程池的实现类(一般使用SimpleThreadPool即可满足几乎所有用户的需求)
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
#指定线程数,至少为1(无默认值)(一般设置为1-100直接的整数合适)
org.quartz.threadPool.threadCount = 25
#设置线程的优先级(最大为java.lang.Thread.MAX_PRIORITY 10,最小为Thread.MIN_PRIORITY 1,默认为5)
org.quartz.threadPool.threadPriority = 5
#============================================================================
# Configure JobStore
#============================================================================
# 信息保存时间 默认值60秒
org.quartz.jobStore.misfireThreshold = 60000
#数据保存方式为数据库持久化
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#数据库代理类,一般org.quartz.impl.jdbcjobstore.StdJDBCDelegate可以满足大部分数据库
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#JobDataMaps是否都为String类型
org.quartz.jobStore.useProperties = false
#数据库别名 随便取
org.quartz.jobStore.dataSource = myDS
#表的前缀,默认QRTZ_
org.quartz.jobStore.tablePrefix = qrtz_
#是否加入集群
org.quartz.jobStore.isClustered = true
#调度实例失效的检查时间间隔
org.quartz.jobStore.clusterCheckinInterval = 20000
#============================================================================
# Configure Datasources
#============================================================================
#数据库引擎
org.quartz.dataSource.myDS.driver = com.mysql.cj.jdbc.Driver
#数据库连接
org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/testdb?characterEncoding=utf8&allowMultiQueries=true&useSSL=false&autoReconnect=true&serverTimezone=UTC
#数据库用户
org.quartz.dataSource.myDS.user = root
#数据库密码
org.quartz.dataSource.myDS.password = root
#允许最大连接
org.quartz.dataSource.myDS.maxConnections = 5
#验证查询sql,可以不设置
org.quartz.dataSource.myDS.validationQuery=select 0 from dual
5.Quartz任务工厂(TaskJobFactory)
@Component
public class TaskJobFactory extends AdaptableJobFactory {
@Autowired
AutowireCapableBeanFactory capableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
//调用父类的方法
Object jobInstance = super.createJobInstance(bundle);
//进行注入
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
6.Quartz配置类(QuartzConfig)
@Configuration
public class QuartzConfig {
@Autowired
DataSource dataSource;
@Autowired
TaskJobFactory jobFactory;
@Bean(name = "SchedulerFactory")
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
//获取配置属性
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
//在quartz.properties中的属性被读取并注入后再初始化对象
propertiesFactoryBean.afterPropertiesSet();
//创建SchedulerFactoryBean
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setQuartzProperties(propertiesFactoryBean.getObject());
factory.setJobFactory(jobFactory);
return factory;
}
/*
* 通过SchedulerFactoryBean获取Scheduler的实例
*/
@Bean(name = "scheduler")
public Scheduler scheduler() throws IOException {
return schedulerFactoryBean().getScheduler();
}
}
7.Quartz服务层接口(QuartzService)
public interface QuartzService {
/**
* 添加任务可以传参数
*
* @param clazz
* @param jobName
* @param groupName
* @param cronExp
* @param param
*/
void addJob(Class clazz, String jobName, String groupName, String cronExp, Map<String, Object> param);
/**
* 暂停任务
*
* @param name
* @param groupName
*/
void pauseJob(String name, String groupName);
/**
* 恢复任务
*
* @param name
* @param groupName
*/
void resumeJob(String name, String groupName);
/**
* 更新任务
*
* @param name
* @param groupName
* @param cronExp
* @param param
*/
void updateJob(String name, String groupName, String cronExp, Map<String, Object> param);
/**
* 删除任务
*
* @param name
* @param groupName
*/
void deleteJob(String name, String groupName);
/**
* 启动所有任务
*/
void startAllJobs();
/**
* 关闭所有任务
*/
void shutdownAllJobs();
}
8.任务调度服务接口实现类(QuartzServiceImpl)
@Service
public class QuartzServiceImpl implements QuartzService {
@Autowired
Scheduler scheduler;
/**
* 创建job,可传参
*
* @param clazz 任务类
* @param name 任务名称
* @param groupName 任务所在组名称
* @param cronExp cron表达式
* @param param map形式参数
*/
@Override
public void addJob(Class clazz, String name, String groupName, String cronExp, Map<String, Object> param) {
try {
// 启动调度器
scheduler.start();
//构建job信息
JobDetail jobDetail = JobBuilder.newJob(((Job) clazz.newInstance()).getClass()).withIdentity(name, groupName).build();
//表达式调度构建器(即任务执行的时间)
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExp);
//按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(name, groupName).withSchedule(scheduleBuilder).build();
//获得JobDataMap,写入数据
if (param != null) {
trigger.getJobDataMap().putAll(param);
}
scheduler.scheduleJob(jobDetail, trigger);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 暂停job
*
* @param name 任务名称
* @param groupName 任务所在组名称
*/
@Override
public void pauseJob(String name, String groupName) {
try {
scheduler.pauseJob(JobKey.jobKey(name, groupName));
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* 恢复job
*
* @param name 任务名称
* @param groupName 任务所在组名称
*/
@Override
public void resumeJob(String name, String groupName) {
try {
scheduler.resumeJob(JobKey.jobKey(name, groupName));
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* job 更新,更新频率和参数
*
* @param name 任务名称
* @param groupName 任务所在组名称
* @param cronExp cron表达式
* @param param 参数
*/
@Override
public void updateJob(String name, String groupName, String cronExp, Map<String, Object> param) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(name, groupName);
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
if (cronExp != null) {
// 表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExp);
// 按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
}
//修改map
if (param != null) {
trigger.getJobDataMap().putAll(param);
}
// 按新的trigger重新设置job执行
scheduler.rescheduleJob(triggerKey, trigger);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* job 删除
*
* @param name 任务名称
* @param groupName 任务所在组名称
*/
@Override
public void deleteJob(String name, String groupName) {
try {
scheduler.pauseTrigger(TriggerKey.triggerKey(name, groupName));
scheduler.unscheduleJob(TriggerKey.triggerKey(name, groupName));
scheduler.deleteJob(JobKey.jobKey(name, groupName));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 启动所有定时任务
*/
@Override
public void startAllJobs() {
try {
scheduler.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 关闭所有定时任务
*/
@Override
public void shutdownAllJobs() {
try {
if (!scheduler.isShutdown()) {
scheduler.shutdown();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
9.需要执行的任务(OrderTimeOutJob )
public class OrderTimeOutJob implements Job {
@Override
public void execute(JobExecutionContext context) {
//获取任务名
String taskName = context.getJobDetail().getKey().getName();
System.out.println("taskName->" + taskName);
//todo:处理执行任务时的业务代码
}
}
10.编写测试类(JobController)
@RestController
public class JobController {
@Autowired
QuartzService service;
/**
* 添加新任务
*
* @return
*/
@RequestMapping("/addJob")
public Object addJob() {
Map<String, Object> resultMap = new HashMap<>();
//任务组名
String groupName = "order";
//任务名
String jobName = "20190724120322389224";
//CRON表达式
String cronExp = "0/10 * * * * ? *";
service.addJob(OrderTimeOutJob.class, jobName, groupName, cronExp, null);
resultMap.put("groupName", groupName);
resultMap.put("jobName", jobName);
resultMap.put("cronExp", cronExp);
return resultMap;
}
/**
* 删除任务
*
* @return
*/
@RequestMapping("/delJob")
public Object delJob() {
Map<String, Object> resultMap = new HashMap<>();
//任务组名
String groupName = "order";
//任务名
String jobName = "20190724120322389224";
service.deleteJob(jobName, groupName);
resultMap.put("groupName", groupName);
resultMap.put("jobName", jobName);
return resultMap;
}
}
11.最后把springboot的配置文件配置好了就可以启动项目测试了
spring:
application:
name: quartz
datasource:
url: jdbc:mysql://localhost:3306/testdb?characterEncoding=utf8&allowMultiQueries=true&useSSL=false&autoReconnect=true&serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
server:
port: 80
完整代码可以参考我的码云项目:https://gitee.com/dongdingzhuo/boot.git