一、准备工作
1.maven的 pom.xml中添加依赖
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz-jobs -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.3.0</version>
</dependency>
2.创建quartz内部的11张表
- 从maven仓库中找到自己要用的版本的 quzrtz jar包,拷贝一份出来,解压;
jar -xvf quartz-2.3.0.jar
- 打开解压包,建表语句在 org/quartz/impl/jdbcjobstore 目录下
3.配置
3.1 配置文件
org.quartz.scheduler.instanceName = TestSchedulerDev
org.quartz.scheduler.instanceId = AUTO
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 100
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
org.quartz.jobStore.misfireThreshold = 60000
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.maxMisfiresToHandleAtATime=10
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 20000
org.quartz.jobStore.dataSource = myDS
org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://172.xx.x.xxx:3306/tmp?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = root
org.quartz.dataSource.myDS.maxConnections = 20
3.2 配置类
import org.quartz.Scheduler;
import org.quartz.ee.servlet.QuartzInitializerListener;
import org.quartz.spi.JobFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import javax.sql.DataSource;
import java.io.IOException;
/**
* @author
* @create 2020-06-16 4:16 PM
*/
@Configuration
public class SchedulerConfig implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private JobFactory jobFactory;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("任务已经启动..."+event.getSource());
}
@Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
//创建SchedulerFactoryBean
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setJobFactory(jobFactory);
factory.setWaitForJobsToCompleteOnShutdown(true);//这样当spring关闭时,会等待所有已经启动的quartz job结束后spring才能完全shutdown。
factory.setOverwriteExistingJobs(false);
factory.setStartupDelay(1);
factory.setConfigLocation(new ClassPathResource("/quartz.properties"));
return factory;
}
/*
* 通过SchedulerFactoryBean获取Scheduler的实例
*/
@Bean(name="scheduler")
public Scheduler scheduler() throws IOException {
return schedulerFactoryBean().getScheduler();
}
@Bean
public QuartzInitializerListener executorListener() {
return new QuartzInitializerListener();
}
}
/**
* 创建job 实例工厂,解决spring注入问题,如果使用默认会导致spring的@Autowired 无法注入问题*/
@Component
public class JobFactory extends AdaptableJobFactory {
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
//调用父类的方法
Object jobInstance = super.createJobInstance(bundle);
//进行注入
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
4.封装job执行的类
@Service
public class QuartzManager {
@Autowired
@Qualifier("scheduler")
private Scheduler scheduler;
/** 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名*/
public void addJob(String jobName, Class jobClass, String time , JobDataMap jobDataMap) {
try {
JobDetail jobDetail = JobBuilder.newJob(jobClass)
.withIdentity(jobName)
.setJobData(jobDataMap)
.requestRecovery(false)
.storeDurably(true)
.build();
CronTrigger cronTrigger = (CronTrigger)TriggerBuilder.newTrigger()
.withIdentity(jobName)
.withSchedule(CronScheduleBuilder.cronSchedule(time))
.build();
scheduler.scheduleJob(jobDetail, cronTrigger);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**添加一个定时任务*/
public void addJob(String jobName, String jobGroupName,
String triggerName, String triggerGroupName, Class jobClass,
String time,JobDataMap jobDataMap) {
try {
JobDetail jobDetail = JobBuilder.newJob(jobClass)
.withIdentity(jobName,jobGroupName)
.setJobData(jobDataMap)
.requestRecovery(false)
.storeDurably(true)
.build();
CronTrigger cronTrigger = (CronTrigger)TriggerBuilder.newTrigger()
.withIdentity(triggerName,triggerGroupName)
.withSchedule(CronScheduleBuilder.cronSchedule(time))
.build();
scheduler.scheduleJob(jobDetail, cronTrigger);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void updateJob(String jobName, String jobGroupName,
String triggerName, String triggerGroupName, Class jobClass,
String time,JobDataMap jobDataMap) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
if(trigger == null){
// 新建job
addJob(jobName,jobGroupName,jobName,jobGroupName,jobClass,time,jobDataMap);
}else{
String oldTime = trigger.getCronExpression();
if(!oldTime.equalsIgnoreCase(time)) {
// 触发器
TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
// 触发器名,触发器组
triggerBuilder.withIdentity(triggerName, triggerGroupName);
triggerBuilder.startNow();
// 触发器时间设定
triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(time));
// 创建Trigger对象
trigger = (CronTrigger) triggerBuilder.build();
// 方式一 :修改一个任务的触发时间
scheduler.rescheduleJob(triggerKey, trigger);
}
}
}catch (Exception e){
throw new RuntimeException(e);
}
}
/** 移除一个任务 */
public void removeJob(String jobName, String jobGroupName,String triggerName, String triggerGroupName) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(triggerName,triggerGroupName);
// 停止触发器
scheduler.pauseTrigger(triggerKey);
// 移除触发器
scheduler.unscheduleJob(triggerKey);
// 删除任务
scheduler.deleteJob(JobKey.jobKey(jobName,jobGroupName));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/** 根据分组删除任务*/
public void removeJobByJobGroup(String jobGroupName){
try{
GroupMatcher<JobKey> matcher = GroupMatcher.groupEquals(jobGroupName);
Set<JobKey> jobkeySet = scheduler.getJobKeys(matcher);
List<JobKey> jobkeyList = new ArrayList<JobKey>();
jobkeyList.addAll(jobkeySet);
// 停止触发器
scheduler.deleteJobs(jobkeyList);
}catch (Exception e){
throw new RuntimeException(e);
}
}
/** 功能:启动所有定时任务 */
public void startJobs() {
try {
scheduler.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/** 功能:关闭所有定时任务*/
public void shutdownJobs() {
try {
if (!scheduler.isShutdown()) {
scheduler.shutdown();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
5. 对外发布dubbo接口,提供远程添加/修改/删除job的服务
public interface JmeterJobService {
/* 新增 */
void addJob(String operator,Integer cronId,String cronExpression);
/* 修改 */
void updateJob(String operator, Integer cronId,String cronExpression);
/* 删除 */
void deleteJob(Integer cronId);
/** 根据组名称批量删除 */
void removeJobByJobGroup(String jobGroupName);
}
package com.byxf.tmp.executor.service.impl;
import com.alibaba.dubbo.config.annotation.Service;
import com.byxf.common.util.SpringBeanUtil;
import com.byxf.tmp.executor.quartz.QuartzManager;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;
/** @create 2020-06-17 12:56 AM
*/
@Component
@Service(version = "1.0.0",timeout = 4000,retries = -1,interfaceClass = JmeterJobService.class)
public class JmeterJobServiceImpl implements Job,JmeterJobService{
private Logger logger = LoggerFactory.getLogger(JmeterJobServiceImpl.class);
@Autowired
private QuartzManager quartzManager;
@Autowired
private JmeterCronRelationInnerService relationInnerService;
@Autowired
private JmeterExecuteService jmeterExecuteService;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// JobDataMap data=context.getTrigger().getJobDataMap();
JobDataMap data=context.getJobDetail().getJobDataMap();
Integer cronId = Integer.valueOf(String.valueOf(data.get("cronId")));
String operator = String.valueOf(data.get("operator"));
List<TmpJmeterCasePo> poList = relationInnerService.getJmeterCaseByCronId(cronId);
List<Integer> jmeterIdList = new ArrayList<Integer>();
for(TmpJmeterCasePo po : poList){
jmeterIdList.add(po.getId());
}
//开始调用执行方法
jmeterExecuteService.executeBatch(jmeterIdList,operator,"3");
}
@Override
public void addJob(String operator,Integer cronId,String cronExpression) {
String idName = "jmeter-"+"-"+cronId;
String groupName = "jmeter-"+"-"+cronId;
logger.info(">>>>>>新增job任务,{},{},{},{}",idName,groupName,cronExpression,this.getClass());
try {
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("operator", operator);
jobDataMap.put("cronId", cronId);
jobDataMap.put("cronExpression", cronExpression);
quartzManager.addJob(idName,groupName,idName,groupName,this.getClass(),cronExpression,jobDataMap);
}catch (Exception e){
logger.error("调度任务" + idName + "启动异常", e);
}
}
@Override
public void updateJob(String operator, Integer cronId,String cronExpression) {
String idName = "jmeter-"+cronId;
String groupName = "jmeter-"+cronId;
logger.info(">>>>>>新增job任务,{},{},{},{}",idName,groupName,cronExpression,this.getClass());
try {
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("operator", operator);
jobDataMap.put("cronId", cronId);
jobDataMap.put("cronExpression", cronExpression);
quartzManager.updateJob(idName,groupName,idName,groupName,this.getClass(),cronExpression,jobDataMap);
}catch (Exception e){
logger.error("调度任务" + idName + "更新异常", e);
}
}
@Override
public void deleteJob(Integer cronId) {
String idName = "jmeter-"+cronId;
logger.info(">>>>>>删除job任务,{}",idName);
try {
SchedulerFactoryBean schedulerFactoryBean = SpringBeanUtil.getContext().getBean(SchedulerFactoryBean.class);
Scheduler scheduler = schedulerFactoryBean.getScheduler();
scheduler.unscheduleJob(new TriggerKey(idName));
scheduler.deleteJob(new JobKey(idName));
}catch (SchedulerException e){
logger.error("调度任务" + idName + "删除异常", e);
}
}
public void removeJobByJobGroup(String jobGroupName) {
quartzManager.removeJobByJobGroup(jobGroupName);
}
}
【tips】
此处对外发布dubbo接口的时候,如果是实现了多个接口,需要指定对外暴露的是那个接口(决定着这个服务对外提供哪个接口的方法让消费者端调用)如果不指定,默认暴露出去的是第一个实现的接口。如果只靠调整实现接口的顺序还是容易出错,因此需要用 interfaceClass 属性指定一个接口。
6. 消费者端调用dubbo服务,操作job。
@Reference(version = "1.0.0")
private JmeterJobService jmeterJobService;
@RequestMapping("jobExecute")
@ResponseBody
public Object jobExecute(HttpServletRequest request,TmpJmeterCronConfigPo cronConfigPo, String jmeterIds) throws Exception {// @RequestParam(value = "jmeterIdArray[]") List<Integer>jmeterIdArray
String[] jmeterIdArr = jmeterIds.split(",");
List list =Arrays.asList(jmeterIdArr);
List<Integer> jmeterIdList = new ArrayList<Integer>();
for(Object o : list){
Integer jmeterId = Integer.valueOf(o.toString());
jmeterIdList.add(jmeterId);
}
String cronExpression = cronConfigPo.getCronExpression();
try{
String operator = clientUserUtil.getUserInfoFromSession(request).getPersonName();
jmeterCronService.insert(jmeterIdList,cronConfigPo,operator);
TmpJmeterCronConfigPo lastCronConfigPo = jmeterCronService.getLastPo();
jmeterJobService.updateJob(operator,lastCronConfigPo.getId(),cronExpression);
return success("定时任务添加成功");
}catch (Exception e){
logger.error(e.getMessage());
return error("500", "定时任务添加异常");
}