Quartz学习整理
Quartz学习整理
2023今天学习的内容是石英钟Quartz定时启动任务的框架
应用场景
618淘宝的0:00秒杀
qq新闻的定时推送
未支付的订单,到时间删除订单
到了指定的时间就执行相应的任务,都需要Quartz框架
Quartz的体系架构
1.任务类
实现Job类,重写execute()方法
2.触发器Trigger
定义时间规则CronTrigger,
3.调度器Schduler
将任务和出发器绑定,调度器会在符合时间规则的条件下,执行任务
Quartz的简单使用
1.新建maven项目
2.pom.xml添加依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Quartz任务调度-->
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
</dependency>
</dependencies>
3.application.yml文件配置文件
server:
port: 8081
spring:
application:
name: QUARTZSERVER
4.项目启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class QuartzApplication {
public static void main(String[] args) {
SpringApplication.run(QuartzApplication.class,args);
}
}
5.任务类
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class FirstJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
//编写要执行的任务即可
//当到达设定的时间后,Quartz就会自动调用此execute方法
//可以通过JobExecutionContext类型的参数,来获取任务的相关信息
//【说明】在企业级项目中,定时任务有很多,为了区分任务,可以给任务分组,设置任务的名字
JobDetail jobDetail=context.getJobDetail();
//jobDetail.getKey().getGroup()
//getKey() 先获取任务
//getGroup() 获取组 字符串类型,代表组的名字
System.out.println("任务所在的组:"+jobDetail.getKey().getGroup());
//getName() 获取的是任务的名字
System.out.println("任务的名字是:"+jobDetail.getKey().getName());
for (int i=1; i<=10; i++){
System.out.println("i="+i);
}
}
}
6.触发器Trigger
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.stereotype.Service;
import java.util.GregorianCalendar;
@Service
public class JobServiceImpl implements JobService {
@Override
public String startJob1() {
String result="";
//声明时间规则
//1、声明触发器
Trigger trigger= TriggerBuilder.newTrigger()
//参数1:触发器的名字
//参数2:触发器所在的组的名字
.withIdentity("trigger1","triggerGroup1")
.startNow() //执行后,触发器立刻开始生效,还可以设置具体的开始时间 startAt(通过GregorianCalendar设置开始时间)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(3) //每间隔多长时间执行一次任务
.repeatForever() //重复的次数:repeatForever()---一直重复 withRepeatCount(重复的次数)
).endAt(new GregorianCalendar(2023,6,19,11,0,0).getTime())
.build();
//bug:结束时间到达后,并不能结束
//2、把要执行的任务 封装在JobDetail中
JobDetail jobDetail= JobBuilder.newJob(FirstJob.class)
.withIdentity("job1","group1")
.build();
//3、声明调度器Scheduler:把触发器和任务绑定在一起
try {
Scheduler scheduler= StdSchedulerFactory.getDefaultScheduler();
//绑定触发器和任务
scheduler.scheduleJob(jobDetail,trigger);
//调度器开始调度
scheduler.start();
result="第一个任务已经成功开启";
} catch (SchedulerException e) {
result="第一个任务开启失败!";
e.printStackTrace();
}
return result;
}
}
[扩展]使用线程池来优化线程的使用
原因:线程的创建和销毁,增加线程的开销,降低了线程的使用效率,
需要线程池来固定线程的数量,防止无限的消耗,最终耗尽系统资源
1、在resources下新建quartz.properties配置文件
quartz在启动的时候,默认就会读取的配置文件
2、在此文件中配置线程池信息
# 指定调度器的类型
org.quartz.scheduler.instanceName=DefaultQuartzScheduler
# 指定线程池的类型
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
# 线程池中线程的数量
org.quartz.threadPool.threadCount=10
# 线程池中线程的优先级
org.quartz.threadPool.threadPriority=5
#非持久化Job ,对执行的任务不进行持久化存储
org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore
触发器Tigger的类型
触发器是用来规定任务的执行时间
1 SimpleTrigger简单时间规则(了解即可)
2 CronTrigger,使用Cron表达式来声明时间规则,从而触发不同的时间场景
6,7位组成,位与位之间用 空格 隔开,分别代表
秒 分 时 日 月 星期 年
各个位置上允许出现的值
3、各个位置上的字符含义
* 可以出现在任意位置上,表示:每一XXX
* * * 20 6 ? 2023
, 表示的是列表,当某个位置上出现多个值的时候,值之间使用,进行分割
10,30 * * 20 6 ? 2023 23年6月20日的每个小时的每分钟的第10秒、30秒的时候分别执行任务
- 表示的范围,是连续的一个区间
0-9 * * 20 6 ? 2023 23哪年6月20日的每个小时的每分钟的第0 1 2 3 4 5 6 7 8 9秒时分别执行任务
0,1,2,3,4,5,6,7,8,9 * * 20 6 ? 2023
/ 步长 每增加多少 每间隔多少
0/5 5/3 * 20 6 ? 2023
? 只能出现在 日期和星期的位置上,当设置了日期或星期中的任意一个值,另一个就可以使用?,表示任意值
星期的取值范围1-7 1表示的是星期日, 2 表示星期1 7对应星期六
L :只能出现在星期、日期的位置上
出现日期的位置上,表示当月的最后一天,自行判断平、润年 及大小月份
出现在星期的位置上: 表示这个星期的最后一天, 指的是周六 7
* * * ? 6 L 2023
出现在星期的位置上:L的前面出现了1-7之间的数字,表示 最后一个星期几
0 30 17 ? * 6L 每个月的最后一个星期五的17:30:00执行任务
W:只能出现在日期位置上,表示离该日期最近的一个工作日
0 0 8 17W 6 ? 2023 23年6月16日 8:00:00执行任务
【说明】规则不能跨月,只能在本月内计算
0 0 8 1W 7 ? 2023 23年7月3日的8:00:00执行,而不是 23年6月30日的8:00:00,因为跨到6月份
LW 只能出现在日期里,表示当月的最后一个工作日
# 该字符只用在星期字段中,"4#2"代表第二个星期3,“5#4”代表第4个星期四
语法 :星期值#第几个
应用CronTrigger来声明时间规则
@Override
public String startJob2() {
String result="";
//声明CronTrigger
Trigger trigger=TriggerBuilder.newTrigger()
.withIdentity("trigger2","triggerGroup1")
.withSchedule(CronScheduleBuilder.cronSchedule("3/5 * 15 19 6 ?"))
.build();
JobDetail jobDetail=JobBuilder.newJob(FirstJob.class)
.withIdentity("job2","group1")
.build();
try {
Scheduler scheduler=StdSchedulerFactory.getDefaultScheduler();
scheduler.scheduleJob(jobDetail,trigger);
scheduler.start();
result="任务2被启动!";
} catch (SchedulerException e) {
result="任务2启动失败!";
e.printStackTrace();
}
return result;
}
任务的控制
@Override
//任务的暂停
public String pauseJob(String name, String goup) {
String result="";
//1、先获取调度器
try {
Scheduler scheduler=StdSchedulerFactory.getDefaultScheduler();
//2、通过调度器来暂定任务
scheduler.pauseJob(JobKey.jobKey(name,goup));
result="任务"+name+"已经被暂停";
} catch (SchedulerException e) {
result="任务"+name+"暂停失败!";
e.printStackTrace();
}
return result;
}
@Override
//任务的恢复
public String resumeJob(String name, String group) {
String result="";
try {
Scheduler scheduler=StdSchedulerFactory.getDefaultScheduler();
scheduler.resumeJob(JobKey.jobKey(name, group));
result="任务"+name+"已经被恢复执行";
} catch (SchedulerException e) {
result="任务"+name+"恢复失败!";
e.printStackTrace();
}
return result;
}
删除单个任务
@Override
public String deleteJob(String name, String group) {
String result="";
try {
Scheduler scheduler=StdSchedulerFactory.getDefaultScheduler();
boolean boolResult= scheduler.deleteJob(JobKey.jobKey(name, group));
if(boolResult){
result="删除任务"+name+"成功!";
}else{
result="删除任务"+name+"失败!";
}
} catch (SchedulerException e) {
result="删除任务"+name+"失败!";
e.printStackTrace();
}
return result;
}
任务的批量暂停、恢复
【注意】批量操作是通过组进行操作,把组名封装在GroupMatcher中
@Override
public String pauseJobs(String group) {
String result="";
try {
Scheduler scheduler=StdSchedulerFactory.getDefaultScheduler();
//把组信息封装在了GroupMatcher中
GroupMatcher<JobKey> groupMatcher=GroupMatcher.groupEquals(group);
scheduler.pauseJobs(groupMatcher);
result="组"+group+"批量暂停成功";
} catch (SchedulerException e) {
result="组"+group+"批量暂停失败";
e.printStackTrace();
}
return result;
}
@Override
public String resumeJobs(String group) {
String result="";
try {
Scheduler scheduler=StdSchedulerFactory.getDefaultScheduler();
//把组信息封装在了GroupMatcher中
GroupMatcher<JobKey> groupMatcher=GroupMatcher.groupEquals(group);
scheduler.resumeJobs(groupMatcher);
result="组"+group+"批量恢复成功";
} catch (SchedulerException e) {
result="组"+group+"批量恢复失败";
e.printStackTrace();
}
return result;
}
在控制器中调用业务逻辑
@RestController
public class JobController {
@Autowired
private Job1Service job1Service;
@GetMapping("/start1")
public String start1(){
return job1Service.startJob();
}
@GetMapping("/start2")
public String start2(){
return job1Service.startJob2();
}
@GetMapping("/pause")
public String pause(String name,String group){
return job1Service.pauseJob(name,group);
}
@GetMapping("/resume")
public String resume(String name,String group){
return job1Service.pauseJob(name,group);
}
@GetMapping("/delete")
public String delete(String name,String group){
return job1Service.deleteJob(name,group);
}
}