Quartz
理论知识
原理
分层时间轮:分为月轮,天轮等等。 每一个任务有精确的时间坐标
原生Quartz
核心组件
定时任务
对应JobDetail类 包含任务 可以传递数据给任务
触发器
对应Trigger类 包含时间安排 可以传递数据给任务
-
SimpleSchedule作为内核
包含第一次执行的时间,执行间隔,重复次数,结束时间(如果算出下一次的执行时间大于等于结束时间那就没有下一次了,如果指定的次数都执行完了还没有到结束时间也不会有下一次了。)。
-
CronSchedule作为内核
对于CronSchedule,包含第一次执行的时间,执行间隔,(没有重复次数,也就是无限次。),结束时间。 Cron表达式在线生成详见链接cron.qqe2.com
小结:定时任务和触发器是一对多的关系
调度器
将定时任务和触发器绑定在一起,并根据触发器来派发任务。 对应Scheduler类 一个调度器完全可以绑定所有的定时任务和触发器了 调度的任务是在另一个线程执行的
SpringBoot集成Quartz
集成之后可以选择将任务等存放到数据库 有了数据库之后就可以集群了 集群思路:刚开始把数据库建好,把任务都存进去,然后再启动集群来执行里面的任务。 集群执行任务的原则:尽可能平均分配任务,同一任务的不同执行可能在不同应用上。 不管集群与否,每个应用启动都先根据配置决定启动时是否初始化数据库(运行sql文件)。
代码部分
原生Quartz
依赖
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
任务类
package com.example.quartz.Job;
import org.quartz.*;
import java.util.Date;
//这个注解使得JobDataMap这个map中的数据在多次调用之间不变
//没有的话则每次调用的时候JobDataMap这个map中的数据会被重置为最初设置的
@PersistJobDataAfterExecution
//禁止并发运行多个这个任务,必须等上一个完成了才能进行下一个(但Job对象每次还是会创建新的).
@DisallowConcurrentExecution
public class MyJob implements Job{
public MyJob(){
System.out.println("+++++++++++++++++");
}
@Override
public void execute(JobExecutionContext context) throws JobExecutionException{
try{
Thread.sleep(2000);
}
catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("现在时间是:"+System.currentTimeMillis());
//这两个变量和设置这个任务的两个变量是相等的
//因此可以来获取之前为它们设置的各种属性
JobDetail jobDetail=context.getJobDetail();
Trigger trigger=context.getTrigger();
System.out.println(trigger.getKey().getName());
//System.out.println("_________________");
//System.out.println(trigger.getStartTime());
//System.out.println(trigger.getEndTime());
//System.out.println(new Date());
//System.out.println(trigger.getNextFireTime());
}
}
需要添加定时任务的时候这么写
package com.example.quartz;
import com.example.quartz.Job.MyJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class QuartzTest{
public static void main(String[] args) throws Exception{
System.out.println(Long.MAX_VALUE);
//JobDataMap是一个Map,它可以被传递给任务使用.
JobDataMap jobDataMap1=new JobDataMap();
jobDataMap1.put("jobDetail-param1",0);
jobDataMap1.put("jobDetail-param2","value2");
//构造JobDetail,核心是任务,其他的可以不设置.
JobDetail jobDetail=JobBuilder.newJob(MyJob.class)
//设置任务名字和组名
.withIdentity("我的第一个任务","我的第一个任务组")
//给任务传的参数放在jobDataMap1中
.usingJobData(jobDataMap1)
.build();
//JobDataMap是一个Map,它可以被传递给任务使用.
JobDataMap jobDataMap2=new JobDataMap();
jobDataMap2.put("trigger-param1","value1");
jobDataMap2.put("trigger-param2","value2");
//构造下面要用的startTime
Calendar calendar=Calendar.getInstance();
calendar.add(Calendar.SECOND,3);
Date startTime=calendar.getTime();
//构造下面要用的endTime
calendar.add(Calendar.SECOND,3);
Date endTime=calendar.getTime();
//构造Trigger,核心是[第一次执行的时间,执行间隔,重复次数,结束时间(如果算出下一次的执行时间大于等于结束时间那就没有下一次了,如果指定的次数都执行完了还没有到结束时间也不会有下一次了.).],
// 其他的可以不设置.
Trigger trigger=TriggerBuilder.newTrigger()
//设置触发器名字和组名
.withIdentity("我的第一个触发器","我的第一个触发器组")
//给任务传的参数放在jobDataMap2中
.usingJobData(jobDataMap2)
////使用SimpleSchedule设置时间间隔和重复次数,默认无数次.
//.withSchedule(SimpleScheduleBuilder.repeatSecondlyForTotalCount(3,2))
//使用CronSchedule设置时间间隔,重复次数为无数次. 下面是Cron表达式
.withSchedule(CronScheduleBuilder.cronSchedule("* * * * * ? *"))
//开始时间
.startAt(startTime)
//结束时间
.endAt(endTime)
.build();
//调度器(用来绑定任务和触发器,并根据触发器来派发任务.)
Scheduler scheduler=StdSchedulerFactory.getDefaultScheduler();
scheduler.scheduleJob(jobDetail,trigger);
//启动调度器
scheduler.start();
//执行2秒
Thread.sleep(2000);
//暂停调度器
scheduler.standby();
//暂停2秒
Thread.sleep(2000);
//再启动调度器
scheduler.start();
//关闭调度器
scheduler.shutdown();
}
}
SpringBoot集成Quartz
依赖
<!--quartz-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
公共配置文件 application.yml
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: mysql
quartz:
#Quartz相关数据存储类型
job-store-type: jdbc
jdbc:
#启动时初始化数据库(运行sql文件)
initialize-schema: always
properties:
org:
quartz:
jobStore:
#集群
isClustered: true
应用1的特定配置文件 application-1.yml
spring:
quartz:
properties:
org:
quartz:
scheduler:
#实例名(集群名,根据集群名来区分任务.)
instanceName: test
#实例id
instanceId: test1
应用2的特定配置文件 application-2.yml
spring:
quartz:
properties:
org:
quartz:
scheduler:
#实例名(集群名,根据集群名来区分任务.)
instanceName: test
#实例id
instanceId: test2
Service
package com.example.quartz.service;
import org.springframework.stereotype.Service;
@Service
public class MyService{
public void testService(){
System.out.println("MyService.testService()");
}
}
任务类
package com.example.quartz.job;
import com.example.quartz.service.MyService;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component
public class SpringMyJob extends QuartzJobBean{
//引入依赖后自动有
@Autowired
private Scheduler scheduler;
//可以直接注入
@Autowired
private MyService myService;
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException{
System.out.println("===========================");
System.out.print("现在在SpringMyJob中:");
myService.testService();
System.out.println("现在时间: "+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
try{
System.out.println("实例名:"+scheduler.getSchedulerName());
System.out.println("实例id:"+scheduler.getSchedulerInstanceId());
}
catch(SchedulerException e){
e.printStackTrace();
}
System.out.println("===========================");
}
}
需要添加定时任务的时候这么写
@RestController
class QuartzApplicationTest{
//引入依赖后自动有
@Autowired
private Scheduler scheduler;
@GetMapping("/test")
public String test() throws Exception{
JobDataMap jobDataMap1=new JobDataMap();
jobDataMap1.put("jobDetail-param1",0);
jobDataMap1.put("jobDetail-param2","value2");
//构造JobDetail,核心是任务,其他的可以不设置.
JobDetail jobDetail=JobBuilder.newJob(SpringMyJob.class)
//设置任务名字和组名
.withIdentity("我的第一个任务","我的第一个任务组")
//给任务传的参数放在jobDataMap1中
.usingJobData(jobDataMap1)
.build();
Trigger trigger=TriggerBuilder.newTrigger()
//设置触发器名字和组名
.withIdentity("我的第一个触发器","我的第一个触发器组")
//使用CronSchedule设置时间间隔,重复次数为无数次. 下面是Cron表达式
.withSchedule(CronScheduleBuilder.cronSchedule("* * * * * ? *"))
.build();
Trigger trigger2=TriggerBuilder.newTrigger()
//设置触发器名字和组名
.withIdentity("我的第二个触发器","我的第一个触发器组")
//使用CronSchedule设置时间间隔,重复次数为无数次. 下面是Cron表达式
.withSchedule(CronScheduleBuilder.cronSchedule("* * * * * ? *"))
.forJob(jobDetail)
.build();
//绑定定时任务和触发器(添加定时任务)
scheduler.scheduleJob(jobDetail,trigger);
scheduler.scheduleJob(trigger2);
return "成功";
}
}