定时任务模块相关
设计思路是:用一个数据库表存下需要执行的定时任务的 编码/名称/描述/corn表达式/调用的链接 等信息,新增一条定时任务表的数据时,将这个定时任务注册到quartz中即可。
1.通用代码
①JobFactory
package com.sinoccdc.devops.scheduler.utils;
//import org.quartz.spi.TriggerFiredBundle;
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
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;
}
}
JobStatusEnum状态的枚举类
package com.sinoccdc.devops.scheduler.utils;
public enum JobStatusEnum {
WAITING,
PAUSED,
RUNNING,
BLOCKED,
ERROR;
public String getCode() {
if(this==WAITING) {
return "waiting";
}else if(this==PAUSED) {
return "paused";
}else if(this==RUNNING) {
return "running";
}else if(this==BLOCKED) {
return "blocked";
}else if(this==ERROR) {
return "error";
}
return null;
}
public String getName() {
if(this==WAITING) {
return "等待";
}else if(this==PAUSED) {
return "暂停";
}else if(this==RUNNING) {
return "运行";
}else if(this==BLOCKED) {
return "阻塞";
}else if(this==ERROR) {
return "错误";
}
return null;
}
}
QuartzConfigration
package com.sinoccdc.devops.scheduler.utils;
import java.io.IOException;
//import java.util.Properties;
import java.util.Properties;
//import org.quartz.Scheduler;
//import org.quartz.spi.JobFactory;
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
@Configuration
public class QuartzConfigration {
@Autowired
private JobFactory jobFactory;
@Bean
public SchedulerFactoryBean schedulerFactoryBean() {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
try {
schedulerFactoryBean.setOverwriteExistingJobs(true);
schedulerFactoryBean.setQuartzProperties(quartzProperties());
schedulerFactoryBean.setJobFactory(jobFactory);
} catch (Exception e) {
e.printStackTrace();
}
return schedulerFactoryBean;
}
// 指定quartz.properties,可在配置文件中配置相关属性
@Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocations(new ClassPathResource("/quartz.properties"));
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
// 创建schedule
@Bean(name = "scheduler")
public Scheduler scheduler() {
return schedulerFactoryBean().getScheduler();
}
}
QuartzManager
package com.sinoccdc.devops.scheduler.utils;
import java.util.List;
import com.sinoccdc.devops.domain.application.scheduler.SchedulerTaskService;
import com.sinoccdc.devops.scheduler.SchedulerTask;
import com.sinoccdc.devops.scheduler.SchedulerTaskRepository;
import org.apache.commons.lang3.StringUtils;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.DateBuilder;
import org.quartz.DateBuilder.IntervalUnit;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/*
import org.springframework.data.domain.Page;
import com.sinoccdc.fiveNet.dao.TaskDao;
import com.sinoccdc.fiveNet.pojo.TaskPo;
import com.chinatransinfo.customCommon.scheduler.dao.TaskDao;
import com.chinatransinfo.customCommon.scheduler.pojo.TaskPo;*/
@Component
public class QuartzManager {
@Autowired
private Scheduler scheduler;
@Autowired
private SchedulerTaskService schedulerTaskService;
@Autowired
private SchedulerTaskRepository schedulerTaskRepository;
/**
* 初始化调度
* @throws Exception
*/
public void initSchedule() throws Exception {
// 这里获取任务信息数据
// List<TaskPo> jobList = taskDao.getTaskList(null, null, null);
List<SchedulerTask> jobList = schedulerTaskRepository.findAll();
for (SchedulerTask task : jobList) {
if (JobStatusEnum.RUNNING.getCode().equals(task.getJobStatus())) {
this.addJob(task);
}
}
}
/**
* 添加任务*/
@SuppressWarnings("unchecked")
public void addJob(SchedulerTask task) {
try {
// 创建jobDetail实例,绑定Job实现类
// 指明job的名称,所在组的名称,以及绑定job类
Class<? extends Job> jobClass = (Class<? extends Job>) (Class.forName(task.getBeanClass()).newInstance().getClass());
// 任务名称和组构成任务key
//JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(task.getJobCode(), task.getJobGroup()).build();
//任务的主键构成任务key
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(task.getJobCode(), task.getJobGroup()).build();
// 定义调度触发规则
Trigger trigger;
if(StringUtils.isEmpty(task.getCronExpression())) {
trigger = TriggerBuilder.newTrigger().withIdentity(task.getJobCode(), task.getJobGroup())// 触发器key
.startAt(DateBuilder.futureDate(1, IntervalUnit.SECOND))
.withSchedule(
SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(3)
.withRepeatCount(0)).build();
}else {
// 使用cornTrigger规则
trigger = TriggerBuilder.newTrigger().withIdentity(task.getJobCode(), task.getJobGroup())// 触发器key
.startAt(DateBuilder.futureDate(1, IntervalUnit.SECOND))
.withSchedule(CronScheduleBuilder.cronSchedule(task.getCronExpression())).startNow().build();
}
// 把作业和触发器注册到任务调度中
scheduler.scheduleJob(jobDetail, trigger);
// 启动
if (!scheduler.isShutdown()) {
scheduler.start();
task.setJobStatus(JobStatusEnum.RUNNING.getCode());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 暂停任务
* @param task
* @throws SchedulerException
*/
public void pauseJob(SchedulerTask task) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(task.getJobCode(), task.getJobGroup());
scheduler.pauseJob(jobKey);
task.setJobStatus(JobStatusEnum.PAUSED.getCode());
try {
schedulerTaskService.saveSchedulerTask(task);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 恢复任务
* @param task
* @throws SchedulerException
*/
public void resumeJob(SchedulerTask task) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(task.getJobCode(), task.getJobGroup());
scheduler.resumeJob(jobKey);
task.setJobStatus(JobStatusEnum.RUNNING.getCode());
try {
schedulerTaskService.saveSchedulerTask(task);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 删除任务
* @param task
* @throws SchedulerException
*/
public void deleteJob(SchedulerTask task) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(task.getJobCode(), task.getJobGroup());
scheduler.deleteJob(jobKey);
try {
schedulerTaskService.deleteSchedulerTaskById(task.getId());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 立即出发job
* @param task
* @throws SchedulerException
*/
public void runJobNow(SchedulerTask task) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(task.getJobCode(), task.getJobGroup());
scheduler.triggerJob(jobKey);
}
/**
* 更新任务cron
* @param task
* @throws SchedulerException
*/
public void updateJobCron(SchedulerTask task) throws SchedulerException {
TriggerKey triggerKey = TriggerKey.triggerKey(task.getJobCode(), task.getJobGroup());
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(task.getCronExpression());
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
scheduler.rescheduleJob(triggerKey, trigger);
}
}
ScheduleJobInitListener
package com.sinoccdc.devops.scheduler.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Order(value = 1)
public class ScheduleJobInitListener implements CommandLineRunner{
@Autowired
QuartzManager scheduleJobService;
@Override
public void run(String... args) throws Exception {
try {
scheduleJobService.initSchedule();
} catch (Exception e) {
e.printStackTrace();
}
}
}
TaskRedisData
package com.sinoccdc.devops.scheduler.utils;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.io.Serializable;
import java.util.Date;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class TaskRedisData implements Serializable {
//完成百分比
String completePercent;
//完成时间
Date completeTime;
//发生错误的标识
String failTag;
//其他信息
String message;
}
TaskStandard
package com.sinoccdc.devops.scheduler.utils;
//任务规范
public interface TaskStandard {
//确定任务单元
Long taskUnit();
//执行成功后更新日志记录的完成时间
}
TaskUtils
package com.sinoccdc.devops.scheduler.utils;
//import org.quartz.spi.TriggerFiredBundle;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonParser;
import com.sinoccdc.devops.utils.common.RedisUtil;
import org.apache.commons.collections4.map.HashedMap;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.stereotype.Component;
import springfox.documentation.spring.web.json.Json;
import javax.annotation.Resource;
import java.util.Map;
@Component
public class TaskUtils {
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
@Resource
private RedisUtil redisUtil;
//按照key向redis放数据
public Boolean putDataToRedis(String JobCode, TaskRedisData taskRedisData){
HashedMap<String, Object> map = new HashedMap<>();
//完成百分比
map.put("completePercent",taskRedisData.getCompletePercent());
//完成时间
map.put("completeTime",taskRedisData.getCompleteTime());
//发生错误的标识
map.put("failTag",taskRedisData.getFailTag());
//其他信息
map.put("message",taskRedisData.getMessage());
return redisUtil.hmset(JobCode, map);
}
//按照key向redis拿数据
public TaskRedisData getDataFromRedisByJobCode(String JobCode) {
if (StringUtils.isEmpty(JobCode)) {
throw new RuntimeException("JobCode不能为空");
} else {
if (redisUtil.get(JobCode) != null) {
Object value = redisUtil.get(JobCode);
TaskRedisData taskRedisData = (TaskRedisData) value;
return taskRedisData;
} else {
return null;
}
}
}
//按照key向redis拿数据
public Map<Object, Object> getMapByJobCode(String JobCode) {
if(StringUtils.isEmpty(JobCode)){
throw new RuntimeException("JobCode不能为空");
}else{
return redisUtil.hmget(JobCode);
}
}
}
配置文件quartz.properties
2.数据库设计
-- ----------------------------
-- Table structure for TC_SYS_TASK
-- ----------------------------
DROP TABLE "DEVOPS"."TC_SYS_TASK";
CREATE TABLE "DEVOPS"."TC_SYS_TASK" (
"ID" VARCHAR2(32 BYTE) NOT NULL ,
"PID" VARCHAR2(32 BYTE) NULL ,
"JOB_GROUP" VARCHAR2(100 BYTE) NULL ,
"JOB_CODE" VARCHAR2(100 BYTE) NULL ,
"JOB_NAME" VARCHAR2(255 BYTE) NULL ,
"DESCRIPTION" VARCHAR2(255 BYTE) NULL ,
"CRON_EXPRESSION" VARCHAR2(100 BYTE) NULL ,
"BEAN_CLASS" VARCHAR2(100 BYTE) NULL ,
"SERVICE_INTERFACE" VARCHAR2(300 BYTE) NULL ,
"SERVICE_INTERFACE_PARAMS" VARCHAR2(1000 BYTE) NULL ,
"JOB_STATUS" VARCHAR2(20 BYTE) NULL ,
"LAST_RUN_TIME" DATE NULL ,
"NEXT_RUN_TIME" DATE NULL ,
"CREATE_USER" VARCHAR2(100 BYTE) NULL ,
"CREATE_TIME" DATE NULL ,
"UPDATE_USER" VARCHAR2(100 BYTE) NULL ,
"UPDATE_TIME" DATE NULL ,
"REMARK" VARCHAR2(128 BYTE) NULL ,
"DELETE_FLAG" NUMBER(1) NULL ,
"ORDER_NUM" NUMBER(10) NULL
)
LOGGING
NOCOMPRESS
NOCACHE
;
COMMENT ON TABLE "DEVOPS"."TC_SYS_TASK" IS '定时任务配置';
COMMENT ON COLUMN "DEVOPS"."TC_SYS_TASK"."ID" IS '主键';
COMMENT ON COLUMN "DEVOPS"."TC_SYS_TASK"."PID" IS '父任务ID';
COMMENT ON COLUMN "DEVOPS"."TC_SYS_TASK"."JOB_GROUP" IS '工作组';
COMMENT ON COLUMN "DEVOPS"."TC_SYS_TASK"."JOB_CODE" IS '任务编码';
COMMENT ON COLUMN "DEVOPS"."TC_SYS_TASK"."JOB_NAME" IS '任务名称';
COMMENT ON COLUMN "DEVOPS"."TC_SYS_TASK"."DESCRIPTION" IS '任务描述';
COMMENT ON COLUMN "DEVOPS"."TC_SYS_TASK"."CRON_EXPRESSION" IS 'cron表达式';
COMMENT ON COLUMN "DEVOPS"."TC_SYS_TASK"."BEAN_CLASS" IS '任务执行时调用哪个类的方法 包名+类名';
COMMENT ON COLUMN "DEVOPS"."TC_SYS_TASK"."SERVICE_INTERFACE" IS '服务接口';
COMMENT ON COLUMN "DEVOPS"."TC_SYS_TASK"."SERVICE_INTERFACE_PARAMS" IS '接口参数';
COMMENT ON COLUMN "DEVOPS"."TC_SYS_TASK"."JOB_STATUS" IS '工作状态';
COMMENT ON COLUMN "DEVOPS"."TC_SYS_TASK"."LAST_RUN_TIME" IS '上一次执行时间';
COMMENT ON COLUMN "DEVOPS"."TC_SYS_TASK"."NEXT_RUN_TIME" IS '预计下一次执行时间';
COMMENT ON COLUMN "DEVOPS"."TC_SYS_TASK"."CREATE_USER" IS '创建人';
COMMENT ON COLUMN "DEVOPS"."TC_SYS_TASK"."CREATE_TIME" IS '创建时间';
COMMENT ON COLUMN "DEVOPS"."TC_SYS_TASK"."UPDATE_USER" IS '更新人';
COMMENT ON COLUMN "DEVOPS"."TC_SYS_TASK"."UPDATE_TIME" IS '更新时间';
COMMENT ON COLUMN "DEVOPS"."TC_SYS_TASK"."DELETE_FLAG" IS '删除标记';
-- ----------------------------
-- Indexes structure for table TC_SYS_TASK
-- ----------------------------
-- ----------------------------
-- Uniques structure for table TC_SYS_TASK
-- ----------------------------
ALTER TABLE "DEVOPS"."TC_SYS_TASK" ADD UNIQUE ("JOB_GROUP", "JOB_CODE", "JOB_NAME");
-- ----------------------------
-- Checks structure for table TC_SYS_TASK
-- ----------------------------
ALTER TABLE "DEVOPS"."TC_SYS_TASK" ADD CHECK ("ID" IS NOT NULL);
ALTER TABLE "DEVOPS"."TC_SYS_TASK" ADD CHECK ("ID" IS NOT NULL);
-- ----------------------------
-- Primary Key structure for table TC_SYS_TASK
-- ----------------------------
ALTER TABLE "DEVOPS"."TC_SYS_TASK" ADD PRIMARY KEY ("ID");
3.ScheduleDistribute实现类
主要完成定时任务的具体实现,在此处为,从task表拿到任务实体具体内容并运行
package com.sinoccdc.devops.scheduler;
import com.sinoccdc.devops.domain.application.scheduler.SchedulerTaskLogService;
import com.sinoccdc.devops.domain.application.scheduler.SchedulerTaskService;
import com.sinoccdc.devops.portal.util.HttpUtil;
import com.sinoccdc.devops.web.page.scheduler.payload.SchedulerTaskLogPayload;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Date;
public class ScheduleDistribute implements Job {
@Autowired
private SchedulerTaskService schedulerTaskService;
@Autowired
private SchedulerTaskLogService schedulerTaskLogService;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobKey jd=context.getJobDetail().getKey();
//根据找到task任务实体,找到要的参数
SchedulerTask task = schedulerTaskService.findByJobGroupAndJobCode(jd.getGroup(), jd.getName());
//生成日志记录
SchedulerTaskLog taskLog = new SchedulerTaskLog();
taskLog.setJobCode(task.getJobCode());
taskLog.setJobGroup(task.getJobGroup());
taskLog.setJobName(task.getJobName());
taskLog.setTriggerMode(1);//自动
taskLog.setStartTime(new Date());
//集成http 的客户端,调用链接
String serviceInterface = task.getServiceInterface();
String params = task.getServiceInterfaceParams();
//String request = HttpsUtils.httpsRequest(serviceInterface, "POST", params);
//开启一个新线程去执行真正需要进行的任务
Thread t = new Thread(new Runnable(){
public void run(){
// run方法具体重写
//HttpUtil.sendPost(serviceInterface, params, false)
HttpUtil.sendGet(serviceInterface+"/"+params);
}});
t.start();
//完成
schedulerTaskLogService.saveSchedulerTaskLog(taskLog);
}
}
4.其他具体方法
比如从controller->service->dao->pojo的具体方法,都是围绕task表的增删查改
其中注意:
新增完task的数据后要把定时任务注册到quartz中去定时执行;
具体参照quartzManager中封装的各种方法
//开启定时任务
quartzManager.addJob(schedulerTask);
//更新定时任务
quartzManager.updateJobCron(schedulerTask);
//删除定时任务
quartzManager.deleteJob(task);