1. Quartz搭建集群
1.1 背景
- 集群只有一台服务器调用定时任务,浪费了其他机器性能
- 集群中的每台服务器调用定时任务,但是一个任务只会分配到一个集群中的一台服务器上
1.2 搭建Quartz集群
1.2.1 配置application.yml文件
配置主配置文件application.yml
spring:
profiles:
# 告知spring读取自定义配置文件
include: 1,2
datasource:
url: jdbc:mysql://127.0.0.1/quartzTest?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8
driver-class-name: com.mysql.jdbc.Driver
username: root
password: root
quartz:
job-store-type: JDBC
jdbc:
initialize-schema: always
配置Quartz集群文件
- application1.yml
spring:
quartz:
properties:
# 设置Quartz集群名称
org.quartz.scheduler.instanceName: OrderService
# 设置Quartz集群标识ID
org.quartz.scheduler.instanceId: Order_1
# 是否设为集群模式
org.quartz.jobStore.isClustered: true
# 设置线程数
org.quartz.threadPool.threadCount: 3
- application2.yml
spring:
quartz:
properties:
# 设置Quartz集群名称
org.quartz.scheduler.instanceName: OrderService
# 设置Quartz集群标识ID
org.quartz.scheduler.instanceId: Order_2
# 是否设为集群模式
org.quartz.jobStore.isClustered: true
# 设置线程数
org.quartz.threadPool.threadCount: 3
1.2.2 配置Project Structure
- ShareQuartzApplication1
- ShareQuartzApplication2
1.2.3 编辑JobClusterInit类
实现多个任务依次调用
package com.mochasoft.jobconfig;
import com.mochasoft.job.SpringBootJob1;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class JobClusterInit {
// springboot自动注入Scheduler调度器
@Autowired
private Scheduler scheduler;
/**
* @PostConstruct
* 修饰非静态void()方法。该方法会在服务器家在Servlet时候运行,并且只会被服务器执行一次。
* 该方法在构造函数之后执行,init()方法之前执行。
* 该注解的方法在整个Bean初始化中的执行顺序:
* Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)
* */
@PostConstruct
public void initJob() throws SchedulerException {
startJob1("job-1", "trigger-1");
startJob1("job-2", "trigger-2");
startJob1("job-3", "trigger-3");
}
private void startJob1(String jobName, String triggerName) throws SchedulerException {
// 创建JobDetail
JobDetail jobDetail = JobBuilder.newJob(SpringBootJob1.class)
.withIdentity(jobName)
.build();
// 创建Trigger
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(triggerName)
.startNow() // 立即开始
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(10)) // 每隔10s执行一次,一直执行下去
.build();
// 调度器执行
scheduler.scheduleJob(jobDetail, trigger);
}
}
1.2.4 配置主启动类
package com.mochasoft;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import javax.annotation.PostConstruct;
import java.util.StringJoiner;
@SpringBootApplication
public class ShareQuartzApplication {
@Autowired
private Scheduler scheduler;
@Value("${spring.quartz.properties.org.quartz.scheduler.instanceId}")
private String sInstanceId;
// 打印当前执行Quartz集群ID
@PostConstruct
public void getInstanceId() {
String instanceName = "";
String instanceId = "";
try {
instanceName = scheduler.getMetaData().getSchedulerName();
instanceId = scheduler.getMetaData().getSchedulerInstanceId();
} catch (SchedulerException e) {
e.printStackTrace();
}
System.out.println(
new StringJoiner("|")
.add(sInstanceId) // spring获取到的instanceId
.add(instanceName) // Quartz获取的instanceName
.add(instanceId) // Quartz获取的instanceId
);
}
public static void main(String[] args) {
SpringApplication.run(ShareQuartzApplication.class, args);
}
}
1.2.5 启动两个启动类,查看控制台输出
- ShareQuartzApplication1
- ShareQuartzApplication2
分析结果 - 没有真正实现集群模式功能实现
- 时间出现混乱(每次启动都会从新删除数据库表,导致时间计时出错)
1.3 实现Quartz集群
1.3.1 启动ShareQuartzApplication1
启动完成后
- 修改application.yml配置文件中自动重建quartz表的配置,此时表中已经有了定时任务
- 因为数据库表中已经有任务,则注掉@Component注解
1.3.2 重启两个启动类,查看控制台输出
查看两个输出结果发现,实现了集群效果,但是读取的配置文件出现了问题-无法正常读取到指定的ymk配置:
解决方案:在spring前面添加“–”,使指定配置路径生效
1.4 实现Quartz分区
1.4.1 背景
当定时任务过多时,会导致性能下降,为了提升性能,在硬件与软件版本固定的情况下,可以通过分区的形式来提升性能
1.4.2 编辑yml配置
application-dev.yml
1.4.3 编辑JobClusterPartitionInit类
package com.mochasoft.jobconfig;
import com.mochasoft.job.SpringBootJob1;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* 分析:
* 该实例化方法实际只执行了一次,可以将初始化的方法实现封装,通过开始调用生成即可
*
*
* */
@Component
public class JobClusterPartitionInit {
@Value("${spring.quartz.properties.org.quartz.scheduler.instanceName}")
private String instanceName;
// springboot自动注入Scheduler调度器
@Autowired
private Scheduler scheduler;
/**
* @PostConstruct
* 修饰非静态void()方法。该方法会在服务器家在Servlet时候运行,并且只会被服务器执行一次。
* 该方法在构造函数之后执行,init()方法之前执行。
* 该注解的方法在整个Bean初始化中的执行顺序:
* Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)
* */
@PostConstruct
public void initJob() throws SchedulerException {
startJob1("job-1", "trigger-1","OrderService-dev");
startJob1("job-2", "trigger-2","OrderService-test");
startJob1("job-3", "trigger-3","OrderService-dev");
}
private void startJob1(String jobName, String triggerName,String partition) throws SchedulerException {
// 如果集群名称与传入的名称不一致,则直接跳过
if (!instanceName.equals(partition)) {
return;
}
// 创建JobDetail
JobDetail jobDetail = JobBuilder.newJob(SpringBootJob1.class)
.usingJobData("instanceName", partition)
.withIdentity(jobName)
.build();
// 创建Trigger
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(triggerName)
.startNow() // 立即开始
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5)) // 每隔10s执行一次,一直执行下去
.build();
// 调度器执行
scheduler.scheduleJob(jobDetail, trigger);
}
}