最近有个需求,需要动态的去管理任务的调度,参考了网上的资料后就设计了如下方案。
1、设计一个任务表
CREATE TABLE `api_scheduler` (
`job_name` varchar(128) NOT NULL COMMENT '任务名称',
`trigger_name` varchar(128) NOT NULL COMMENT '触发器名称',
`job_group_name` varchar(128) NOT NULL COMMENT '任务组',
`trigger_group_name` varchar(128) NOT NULL COMMENT '触发器组',
`is_vaild` varchar(1) NOT NULL DEFAULT '1' COMMENT '是否有效 1 有效 0无效',
`class_path` varchar(128) NOT NULL COMMENT '类路径',
`is_restart` varchar(1) NOT NULL DEFAULT '0' COMMENT '是否重启该任务 1 重启 0不重启',
`corn` varchar(256) NOT NULL COMMENT 'corn表达式',
`restart_time` datetime DEFAULT NULL COMMENT '重启时间',
`last_run_time` datetime DEFAULT NULL COMMENT '最后一次运行时间',
PRIMARY KEY (`job_name`,`trigger_name`),
UNIQUE KEY `job_name` (`job_name`),
UNIQUE KEY `trigger_name` (`trigger_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
然后再增加一个动态读取表里corn表达式的方法,然后根据corn动态的去改变调度。
2、注入scheduler
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;
@Component("quartzJobFactory")
public class QuartzJobFactory extends AdaptableJobFactory {
//这个对象Spring会帮我们自动注入进来,也属于Spring技术范畴.
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
//调用父类的方法
Object jobInstance = super.createJobInstance(bundle);
//进行注入,这属于Spring的技术,不清楚的可以查看Spring的API.
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
import org.quartz.Scheduler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
@Configuration
public class QuartzConfiguration {
//注入scheduler到spring,在quartzManege会用到
@Bean(name = "scheduler")
public Scheduler scheduler(QuartzJobFactory quartzJobFactory) throws Exception {
SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
factoryBean.setJobFactory(quartzJobFactory);
factoryBean.afterPropertiesSet();
Scheduler scheduler = factoryBean.getScheduler();
scheduler.start();
return scheduler;
}
}
解决在job里注入bean使用为null的问题
3、实现方法 每秒读取一次
@Scheduled(cron = "* * * * * ?")
public void run() throws Exception {
// log.info("The time is now {}", dateFormat.format(new Date()));
// ===================================服务上的算法调用===================================
List listScheduler = apiService.getSchedulerList();
for(ApiScheduler api:listScheduler){
try{
String state = quartzManage.getJobState(api.getTriggerName(),api.getTriggerGroupName());
// log.info(" 任务 "+api.getJobName()+" 的状态为 "+state);
// NONE(没有该job), NORMAL(正常), PAUSED(暂停), COMPLETE(完成), ERROR(错误), BLOCKED(阻塞)
//如果任务有效
if(StringUtils.equals(isVaild_yes,api.getIsVaild())){
//如果任务需要重启 修改了corn后需要重启
if(StringUtils.equals(api.getIsRestart(),isRestart_yes)){
log.info(" 任务 "+api.getJobName()+" 开始重启 ");
//需要重启
quartzManage.removeJob(api.getJobName(),api.getJobGroupName(),api.getTriggerName(),api.getTriggerGroupName());
doJob(api.getClassPath(),api.getJobName(),api.getJobGroupName(),api.getTriggerName(),api.getTriggerGroupName(),api.getCorn());
//更新状态
apiService.updateRestartTime(api.getJobName());
}else{
//不需要重启
//判断任务是否存在 如果不存在 则添加
if(StringUtils.equals(state,"NONE")){
log.info(" 任务 "+api.getJobName()+" 开始启动 ");
doJob(api.getClassPath(),api.getJobName(),api.getJobGroupName(),api.getTriggerName(),api.getTriggerGroupName(),api.getCorn());
}else if(StringUtils.equals(state,"PAUSED")||StringUtils.equals(state,"ERROR")){
log.info(" 任务 "+api.getJobName()+" 重新启动 ");
quartzManage.removeJob(api.getJobName(),api.getJobGroupName(),api.getTriggerName(),api.getTriggerGroupName());
doJob(api.getClassPath(),api.getJobName(),api.getJobGroupName(),api.getTriggerName(),api.getTriggerGroupName(),api.getCorn());
}
}
}else {
if(!StringUtils.equals(state,"NONE")){
log.info(" 任务 "+api.getJobName()+" 移除 ");
quartzManage.removeJob(api.getJobName(),api.getJobGroupName(),api.getTriggerName(),api.getTriggerGroupName());
}
}
}catch (Exception e){
log.error("任务"+api.getJobName()+"调度出现异常,异常信息为:"+e.getMessage());
}
}
// log.info("-------------------------------------------------------------------------------------------------------------------");
// ===================================服务上的算法调用===================================
}
/**
* 添加任务调度
* @param classPath
* @param jobName
* @param jobGroupName
* @param triggerName
* @param triggerGroupNam
* @param corn
* @throws ClassNotFoundException
*/
public void doJob(String classPath,String jobName,String jobGroupName,String triggerName,String triggerGroupNam,String corn) throws ClassNotFoundException {
Class> jobClass = Class.forName(classPath);
Map jobMap = new HashMap<>();
QuartzManager.addJob(jobName,jobGroupName,triggerName,triggerGroupNam, jobClass,corn,jobMap);
}
4、任务管理 (参考网上资料 然后加入了一些自己的方法)
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 任务调度管理
* gaoxueyong 2018.4.13
*/
@Component
public class QuartzManager {
@Autowired
private Scheduler scheduler;
/**
* @Description: 添加一个定时任务
*
* @param jobName 任务名
* @param jobGroupName 任务组名
* @param triggerName 触发器名
* @param triggerGroupName 触发器组名
* @param jobClass 任务
* @param cron 时间设置,参考quartz说明文档
* @param jobMap 参数
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public void addJob(String jobName, String jobGroupName,
String triggerName, String triggerGroupName, Class jobClass,
String cron, Map jobMap) {
try {
// Scheduler sched = schedulerFactory.getScheduler();
// 任务名,任务组,任务执行类
JobDetail jobDetail= JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();
//放置参数
if(jobMap!=null){
for(String key:jobMap.keySet()){
jobDetail.getJobDataMap().put(key,jobMap.get(key));
}
}
jobDetail.getJobDataMap().put("jobName",jobName);
// 触发器
TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger();
// 触发器名,触发器组
triggerBuilder.withIdentity(triggerName, triggerGroupName);
triggerBuilder.startNow();
// 触发器时间设定
triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
// 创建Trigger对象
CronTrigger trigger = (CronTrigger) triggerBuilder.build();
// 调度容器设置JobDetail和Trigger
scheduler.scheduleJob(jobDetail, trigger);
// 启动
if (!scheduler.isShutdown()) {
scheduler.start();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 修改一个任务的触发时间
*
* @param jobName
* @param jobGroupName
* @param triggerName 触发器名
* @param triggerGroupName 触发器组名
* @param cron 时间设置,参考quartz说明文档
*/
public void modifyJobTime(String jobName,
String jobGroupName, String triggerName, String triggerGroupName,
String cron) {
try {
// Scheduler sched = schedulerFactory.getScheduler();
TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
if (trigger == null) {
return;
}
String oldTime = trigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(cron)) {
/** 方式一 :调用 rescheduleJob 开始 */
// 触发器
TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger();
// 触发器名,触发器组
triggerBuilder.withIdentity(triggerName, triggerGroupName);
triggerBuilder.startNow();
// 触发器时间设定
triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
// 创建Trigger对象
trigger = (CronTrigger) triggerBuilder.build();
// 方式一 :修改一个任务的触发时间
scheduler.rescheduleJob(triggerKey, trigger);
/** 方式一 :调用 rescheduleJob 结束 */
/** 方式二:先删除,然后在创建一个新的Job */
//JobDetail jobDetail = sched.getJobDetail(JobKey.jobKey(jobName, jobGroupName));
//Class extends Job> jobClass = jobDetail.getJobClass();
//removeJob(jobName, jobGroupName, triggerName, triggerGroupName);
//addJob(jobName, jobGroupName, triggerName, triggerGroupName, jobClass, cron);
/** 方式二 :先删除,然后在创建一个新的Job */
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 移除一个任务
*
* @param jobName
* @param jobGroupName
* @param triggerName
* @param triggerGroupName
*/
public void removeJob(String jobName, String jobGroupName,
String triggerName, String triggerGroupName) {
try {
// Scheduler sched = schedulerFactory.getScheduler();
TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
if(triggerKey!=null){
scheduler.pauseTrigger(triggerKey);// 停止触发器
scheduler.unscheduleJob(triggerKey);// 移除触发器
scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName));// 删除任务
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description:启动所有定时任务
*/
public void startJobs() {
try {
// Scheduler sched = schedulerFactory.getScheduler();
scheduler.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description:关闭所有定时任务
*/
public void shutdownJobs() {
try {
// Scheduler sched = schedulerFactory.getScheduler();
if (!scheduler.isShutdown()) {
scheduler.shutdown();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 判断当前job是否存在
* @param triggerName
* @param triggerGroupName
* @return NONE(没有该job), NORMAL(正常), PAUSED(暂停), COMPLETE(完成), ERROR(错误), BLOCKED(阻塞);
*/
public String getJobState(String triggerName, String triggerGroupName){
String flag = null;
try {
// Scheduler scheduler = schedulerFactory.getScheduler();
TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
Trigger.TriggerState state = scheduler.getTriggerState(triggerKey);
flag = state.toString();
}catch (Exception e){
flag = "ERROR";
}finally {
return flag;
}
}
}
5、具体执行的任务JOB
/**
* 具体任务
*/
public class OtherAsapJob implements Job{
private final Logger log = LoggerFactory.getLogger(OtherAsapJob.class);
@Autowired
private ApiService apiService;
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
String jobName = jobDataMap.get("jobName").toString();
log.info("job " +jobName+" is start");
List listCategory = apiService.getApiCategoryList(jobName);
List listInvoking = apiService.getApiInvokingParamList(jobName);
try {
DoExcuteJob.start(listCategory,listInvoking);
} catch (Exception e) {
throw new RuntimeException(e);
}
//更新最后一次运行的时间
apiService.updateLastRunTime(jobName);
log.info("job " +jobName+" is end");
}
}
所使用的jar包
org.quartz-scheduler
quartz
2.3.0
org.springframework
spring-context-support