Quartz
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序
1、快速入门
(1)通过start.spring.io快速构建spring boot工程,加上quartz依赖
(2)入门程序编写
public class QuartzTest {
public static void main(String[] args) {
try {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
// define the job and tie it to our HelloJob class
// 定义一个需要去执行的job
JobDetail job = newJob(HelloJob.class)
.withIdentity("job1", "group1")
.build();
/**
* 四种触发器:
*
* CalendarIntervalTriggerImpl (org.quartz.impl.triggers)
* SimpleTriggerImpl (org.quartz.impl.triggers)
* DailyTimeIntervalTriggerImpl (org.quartz.impl.triggers)
* CronTriggerImpl (org.quartz.impl.triggers)
*/
// 创建一个触发器,每隔5秒钟触发执行这个job
Trigger trigger = newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(simpleSchedule() // 简单调度
.withIntervalInSeconds(5) // 每隔5秒执行一次
.repeatForever()) // 永远执行
.build();
// 执行这个job
scheduler.scheduleJob(job, trigger);
TimeUnit.SECONDS.sleep(20);
scheduler.shutdown();
}catch (Exception e){
e.printStackTrace();
}
}
}
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("hello,quartz " + DateUtil.format(new Date()) + ",当前线程:" + Thread.currentThread().getName());
}
}
运行结果:
hello,quartz 2021-12-25 10:39:11,当前线程:DefaultQuartzScheduler_Worker-1
hello,quartz 2021-12-25 10:39:16,当前线程:DefaultQuartzScheduler_Worker-2
hello,quartz 2021-12-25 10:39:21,当前线程:DefaultQuartzScheduler_Worker-3
hello,quartz 2021-12-25 10:39:26,当前线程:DefaultQuartzScheduler_Worker-4
hello,quartz 2021-12-25 10:39:31,当前线程:WorkerThread-LastJob
2、触发器
(1)同一个job能够被不同的触发器进行触发(但是一个触发器只能调度一个job)
public class _01_QuartzTest {
public static void main(String[] args) {
try {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
// 定义一个需要去执行的job
JobDetail job = newJob(_01_HelloJob.class)
.withIdentity("job1", "group1")
.build();
// 创建一个触发器,每隔1秒钟触发执行这个job
Trigger trigger1 = newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(simpleSchedule() // 简单调度
.withIntervalInSeconds(1) // 每隔1秒执行一次
.repeatForever()) // 永远执行
.build();
// 创建第二个触发器,每隔1秒钟触发执行这个job
Trigger trigger2 = newTrigger()
.withIdentity("trigger2", "group1")
.forJob("job1","group1") // 第二个触发器 通过jobName和jobGroup名称确定唯一的job
.startNow()
.withSchedule(simpleSchedule() // 简单调度
.withIntervalInSeconds(1) // 每隔1秒执行一次
.repeatForever()) // 永远执行
.build();
// 执行这个job
scheduler.scheduleJob(job, trigger1);
scheduler.scheduleJob(trigger2); // trigger2已经指定了job
TimeUnit.SECONDS.sleep(3);
scheduler.shutdown();
}catch (Exception e){
e.printStackTrace();
}
}
}
public class _01_HelloJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
StringJoiner std = new StringJoiner(" ")
.add("hello,quartz")
.add(DateUtil.format(new Date())) // 执行时候的时间
.add(Thread.currentThread().getName()) // 当前线程的名称
.add(jobExecutionContext.getTrigger().getKey().getName()); // 获取trigger名称TriggerKey(String name, String group)
System.out.println(std);
}
}
结果;
hello,quartz 2021-12-25 11:10:11 DefaultQuartzScheduler_Worker-1 trigger1
hello,quartz 2021-12-25 11:10:11 DefaultQuartzScheduler_Worker-2 trigger2
hello,quartz 2021-12-25 11:10:12 DefaultQuartzScheduler_Worker-3 trigger1
hello,quartz 2021-12-25 11:10:12 DefaultQuartzScheduler_Worker-4 trigger2
hello,quartz 2021-12-25 11:10:13 DefaultQuartzScheduler_Worker-5 trigger1
hello,quartz 2021-12-25 11:10:13 DefaultQuartzScheduler_Worker-6 trigger2
hello,quartz 2021-12-25 11:10:14 WorkerThread-LastJob trigger1
(2)同一个job能够被不同的JobDetail引用
quartz模型
public class _02_QuartzTest {
public static void main(String[] args) {
try {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
// 定义一个需要去执行的job
JobDetail job1 = newJob(_02_HelloJob.class) // 同一个job
.withIdentity("job1", "group1")
.build();
// 定义一个需要去执行的job
JobDetail job2 = newJob(_02_HelloJob.class) // 同一个job
.withIdentity("job2", "group1")
.build();
// 创建一个触发器,每隔1秒钟触发执行这个job
Trigger trigger1 = newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(simpleSchedule() // 简单调度
.withIntervalInSeconds(1) // 每隔1秒执行一次
.repeatForever()) // 永远执行
.build();
// 创建一个触发器,每隔1秒钟触发执行这个job
Trigger trigger2 = newTrigger()
.withIdentity("trigger2", "group1")
.startNow()
.withSchedule(simpleSchedule() // 简单调度
.withIntervalInSeconds(1) // 每隔1秒执行一次
.repeatForever()) // 永远执行
.build();
// 执行这个job
scheduler.scheduleJob(job1, trigger1);
scheduler.scheduleJob(job2,trigger2);
TimeUnit.SECONDS.sleep(3);
scheduler.shutdown();
}catch (Exception e){
e.printStackTrace();
}
}
}
public class _02_HelloJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
StringJoiner std = new StringJoiner(" ")
.add("hello,quartz")
.add(DateUtil.format(new Date())) // 执行时候的时间
.add(Thread.currentThread().getName()) // 当前线程的名称
.add(jobExecutionContext.getTrigger().getKey().getName()) // 获取trigger名称
.add(jobExecutionContext.getJobDetail().getKey().getName()); // 获取JobDetail名称
System.out.println(std);
}
}
结果:
hello,quartz 2021-12-25 11:28:05 DefaultQuartzScheduler_Worker-1 trigger1 job1
hello,quartz 2021-12-25 11:28:05 DefaultQuartzScheduler_Worker-2 trigger2 job2
hello,quartz 2021-12-25 11:28:06 DefaultQuartzScheduler_Worker-3 trigger1 job1
hello,quartz 2021-12-25 11:28:06 DefaultQuartzScheduler_Worker-4 trigger2 job2
hello,quartz 2021-12-25 11:28:07 DefaultQuartzScheduler_Worker-5 trigger1 job1
hello,quartz 2021-12-25 11:28:07 DefaultQuartzScheduler_Worker-6 trigger2 job2
hello,quartz 2021-12-25 11:28:08 DefaultQuartzScheduler_Worker-7 trigger1 job1
hello,quartz 2021-12-25 11:28:08 DefaultQuartzScheduler_Worker-8 trigger2 job2
(3)关于jobDetail和Trigger的name和group
group是用来标记、方便管理的
name是用来标记的
举例:
一个物流系统有三个模块:
运输工具模块
运输人员模块
物流信息模块
job是每天凌晨结算前一天的物流信息
Trigger{group:物流信息,name:日结-auto}
JobDetail{group:物流信息,name:日结}
由于一些原因,需要修改昨天的物流信息,一般会在后台提供手动触发的功能
Trigger{group:物流信息,name:日结-manual}
(4)SimpleTriggerImpl(简单触发器)
(1)可以指定触发的时间间隔
(2)可以指定触发的次数
(5)CronTriggerImpl (org.quartz.impl.triggers)
核心方法
CronScheduleBuilder.cronSchedule("* * * * * ? *") // 传入cron表达式
解释:
(6)传入变量
// 定义一个需要去执行的job
JobDetail job = newJob(_05_HelloJob.class)
.withIdentity("job1", "group1")
.usingJobData("JobDetail_key","JobDetail_value")
.build();
// 创建一个触发器,每隔1秒钟触发执行这个job
Trigger trigger = newTrigger()
.withIdentity("trigger", "group1")
.usingJobData("trigger_key","trigger_value")
.startNow()
.withSchedule(
CronScheduleBuilder.cronSchedule("* * * * * ? *") // 传入cron表达式
)
.build();
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
//获取传入的变量
Trigger trigger = jobExecutionContext.getTrigger();
JobDetail jobDetail = jobExecutionContext.getJobDetail();
JobDataMap triggerJobDataMap = trigger.getJobDataMap();
System.out.println("triggerJobDataMap ==> " + triggerJobDataMap.get("trigger_key"));
//获取传入的变量
JobDataMap jobDetailJobDataMap = jobDetail.getJobDataMap();
System.out.println("jobDetailJobDataMap ==> " + jobDetailJobDataMap.get("JobDetail_key"));
StringJoiner std = new StringJoiner(" ")
.add("hello,quartz")
.add(DateUtil.format(new Date())) // 执行时候的时间
.add(Thread.currentThread().getName()) // 当前线程的名称
.add(jobExecutionContext.getTrigger().getKey().getName()) // 获取trigger名称
.add(jobExecutionContext.getJobDetail().getKey().getName()); // 获取JobDetail名称
System.out.println(std);
}
3、与spring的集成
定义一个job
public class _01_SpringJob extends QuartzJobBean { //继承QuartzJobBean
@Autowired
private SpringAutoService springAutoService;// 注入
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
StringJoiner std = new StringJoiner("---")
.add("hello,quartz")
.add(DateUtil.format(new Date())) // 执行时候的时间
.add(Thread.currentThread().getName());
System.out.println(std);
System.out.println(springAutoService.getHelloSpring());
}
}
@Service
public class SpringAutoService {
public String getHelloSpring(){
return "hello spring";
}
}
(1)第一种方式
@Component
public class JobInit {
@Autowired
public Scheduler scheduler; // 自动注入
@PostConstruct
public void initJob() throws Exception{
JobDetail jobDetail = JobBuilder.newJob(_01_SpringJob.class)
.withIdentity("job1", "group1")
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(simpleSchedule() // 简单调度
.withIntervalInSeconds(1) // 每隔1秒执行一次
.withRepeatCount(3)
).build();
scheduler.scheduleJob(jobDetail,trigger);
}
}
启动springboot工程:
*****************QuartzStudyApplication启动成功************************
hello,quartz---2021-12-25 17:35:30---quartzScheduler_Worker-2
hello spring
hello,quartz---2021-12-25 17:35:31---quartzScheduler_Worker-3
hello spring
hello,quartz---2021-12-25 17:35:32---quartzScheduler_Worker-4
hello spring
(2)第二种方式,会自动关联
@Configuration
public class JobConfig {
@Bean
public JobDetail getJobDetail(){
JobDetail jobDetail = JobBuilder.newJob(_01_SpringJob.class)
.withIdentity("job1", "group1")
.storeDurably()
.build();
return jobDetail;
}
@Bean
public Trigger getTrigger(){
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.forJob("job1","group1")
.startNow()
.withSchedule(simpleSchedule() // 简单调度
.withIntervalInSeconds(1) // 每隔1秒执行一次
.withRepeatCount(3)
).build();
return trigger;
}
}
*****************QuartzStudyApplication启动成功************************
hello,quartz---2021-12-25 17:48:46---quartzScheduler_Worker-1
hello spring
hello,quartz---2021-12-25 17:48:47---quartzScheduler_Worker-2
hello spring
hello,quartz---2021-12-25 17:48:48---quartzScheduler_Worker-3
hello spring
hello,quartz---2021-12-25 17:48:49---quartzScheduler_Worker-4
hello spring
4、持久化
配置如下:
(1) quartz的表和业务库在一起
# 配置数据源
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://127.0.0.1:3306/exercise?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
# quartz持久化 会在exercise中创建quartz表
quartz:
job-store-type: jdbc
jdbc:
initialize-schema: always // 每次启动会重新创建表 初次启动后可以改为never
(2) quartz的表单独在一个库中
@Bean
@QuartzDataSource
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("");
dataSource.setUsername("");
dataSource.setPassword("");
dataSource.setUrl("");
return dataSource;
}
quartz:
properties: # 添加原生配置
org.quartz.threadPool.threadCount: 10
username: root
...
5、集群环境
(1)只让集群中的一台服务器去跑定时任务(会浪费其他服务器的性能)
(2)每台服务器都会执行定时任务,但是一个任务只会分配到一台机器上