一、準備工作
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 [email protected]
* @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", "定時任務添加異常");
}