SpringBoot+Quartz 定时任务调度管理
将定时任务持久化到数据库,方便启动,暂停,删除等操作。
pom:
<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.1</version> </dependency>
配置:
## quartz org.quartz.scheduler.instanceName = DefaultQuartzScheduler org.quartz.scheduler.rmi.export = false org.quartz.scheduler.rmi.proxy = false org.quartz.scheduler.wrapJobExecutionInUserTransaction = false org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount = 10 org.quartz.threadPool.threadPriority = 5 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true org.quartz.jobStore.misfireThreshold =120000 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.dataSource = mysqld org.quartz.dataSource.mysqld.driver = com.mysql.jdbc.Driver org.quartz.dataSource.mysqld.URL = jdbc:mysql://rm-uf6u04q23mt4mx803yo.mysql.rds.aliyuncs.com/zy_store_db_test?characterEncoding=utf-8 org.quartz.dataSource.mysqld.user =zy org.quartz.dataSource.mysqld.password =5Ay3vpXVJxdsm8gB org.quartz.dataSource.mysqld.maxConnections = 5 org.quartz.dataSource.mysqld.validationQuery = select 0 from dual
Controller:
package com.zy.quartz.controller; import com.baomidou.mybatisplus.plugins.Page; import com.zy.core.base.BaseOut; import com.zy.core.base.BoxOut; import com.zy.core.base.ZyBaseController; import com.zy.core.model.BDic; import com.zy.quartz.model.Job; import com.zy.quartz.model.in.JobIn; import com.zy.quartz.service.JobService; import jodd.util.StringPool; import lombok.extern.slf4j.Slf4j; import org.redisson.executor.CronExpression; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; /** * @author xxx * @description 定时任务控制器 * @date 2020/9/27 */ @Slf4j @RestController @RequestMapping("/job/") public class JobController extends ZyBaseController { @Autowired private JobService jobService; /** * @description:任务列表 * @param jobIn * @return: com.zy.core.base.BaseOut * @author: xxx * @Date: 2020/9/27 15:53 */ @PostMapping("list") public BaseOut jobList(@RequestBody JobIn jobIn) { try{ Page<Job> jobList = jobService.findJobs(jobIn); return setSuccessBaseOut(jobList); }catch (Exception e){ log.error("查询定时任务失败",e.getMessage()); return BoxOut.build(BDic.FAIL, "查询定时任务失败"); } } /** * @description:校验CRON表达式是否正确 * @param jobIn * @return: boolean * @author: xxx * @Date: 2020/9/27 16:18 */ @PostMapping("cron/check") public BaseOut checkCron(@RequestBody JobIn jobIn) { try { boolean bol = CronExpression.isValidExpression(jobIn.getCronExpression()); if(bol){ return BoxOut.build(BDic.SUCCESS,"CRON表达式正确"); }else{ return BoxOut.build(BDic.FAIL,"CRON表达式错误"); } } catch (Exception e) { log.error("CRON表达式错误",e.getMessage()); return BoxOut.build(BDic.FAIL, "CRON表达式错误"); } } /** * @description:添加定时任务 * @param job * @return: com.zy.core.base.BaseOut * @author: xxx * @Date: 2020/9/27 16:18 */ @PostMapping("add") public BaseOut addJob(@RequestBody Job job) { try{ jobService.createJob(job); return BoxOut.build(BDic.SUCCESS,"创建定时任务成功"); }catch (Exception e){ log.error("创建定时任务失败",e.getMessage()); return BoxOut.build(BDic.FAIL, "创建定时任务失败"); } } /** * @description:删除定时任务 * @param jobIn * @return: com.zy.core.base.BaseOut * @author: xxx * @Date: 2020/9/27 16:18 */ @PostMapping("del") public BaseOut deleteJob(@RequestBody JobIn jobIn) { try{ String[] ids = jobIn.getJobIds().split(StringPool.COMMA); jobService.deleteJobs(ids); return BoxOut.build(BDic.SUCCESS,"删除定时任务成功"); }catch (Exception e){ log.error("删除定时任务失败",e.getMessage()); return BoxOut.build(BDic.FAIL, "删除定时任务失败"); } } /** * @description:修改定时任务 * @param jobIn * @return: com.zy.core.base.BaseOut * @author: xxx * @Date: 2020/9/27 16:18 */ @PostMapping("edit") public BaseOut updateJob(@RequestBody JobIn jobIn) { try{ jobService.updateJob(jobIn); return BoxOut.build(BDic.SUCCESS,"修改定时任务成功"); }catch (Exception e){ log.error("修改定时任务失败",e.getMessage()); return BoxOut.build(BDic.FAIL, "修改定时任务失败"); } } /** * @description:执行定时任务 * @param jobIn * @return: com.zy.core.base.BaseOut * @author: xxx * @Date: 2020/9/27 16:19 */ @PostMapping("run") public BaseOut runJob(@RequestBody JobIn jobIn) { try{ jobService.run(jobIn.getJobIds()); return BoxOut.build(BDic.SUCCESS,"执行定时任务成功"); }catch (Exception e){ log.error("执行定时任务失败",e.getMessage()); return BoxOut.build(BDic.FAIL, "执行定时任务失败"); } } /** * @description:暂停定时任务 * @param jobIn * @return: com.zy.core.base.BaseOut * @author: xxx * @Date: 2020/9/27 16:19 */ @PostMapping("pause") public BaseOut pauseJob(@RequestBody JobIn jobIn) { try{ jobService.pause(jobIn.getJobIds()); return BoxOut.build(BDic.SUCCESS,"暂停定时任务成功"); }catch (Exception e){ log.error("暂停定时任务失败",e.getMessage()); return BoxOut.build(BDic.FAIL, "暂停定时任务失败"); } } /** * @description:恢复定时任务 * @param jobIn * @return: com.zy.core.base.BaseOut * @author: xxx * @Date: 2020/9/27 16:19 */ @PostMapping("resume") public BaseOut resumeJob(@RequestBody JobIn jobIn) { try { this.jobService.resume(jobIn.getJobIds()); return BoxOut.build(BDic.SUCCESS,"恢复定时任务成功"); }catch (Exception e){ log.error("恢复定时任务失败",e.getMessage()); return BoxOut.build(BDic.FAIL, "恢复定时任务失败"); } } } package com.zy.quartz.controller; import com.baomidou.mybatisplus.plugins.Page; import com.zy.core.base.BaseOut; import com.zy.core.base.BoxOut; import com.zy.core.base.ZyBaseController; import com.zy.core.model.BDic; import com.zy.quartz.model.Job; import com.zy.quartz.model.JobLog; import com.zy.quartz.model.in.JobIn; import com.zy.quartz.model.in.JobLogIn; import com.zy.quartz.service.JobLogService; import com.zy.quartz.service.JobService; import jodd.util.StringPool; import lombok.extern.slf4j.Slf4j; import org.redisson.executor.CronExpression; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author xxx * @description 定时任务控制器 * @date 2020/9/27 */ @Slf4j @RestController @RequestMapping("/job/log/") public class JobLogController extends ZyBaseController { @Autowired private JobLogService jobLogService; /** * @description:任务列表 * @param jobLogIn * @return: com.zy.core.base.BaseOut * @author: xxx * @Date: 2020/9/27 15:53 */ @PostMapping("list") public BaseOut jobList(@RequestBody JobLogIn jobLogIn) { try{ Page<JobLog> jobList = jobLogService.findJobLogs(jobLogIn); return setSuccessBaseOut(jobList); }catch (Exception e){ log.error("查询任务日志失败",e.getMessage()); return BoxOut.build(BDic.FAIL, "查询任务日志失败"); } } /** * @description:删除定时任务 * @param logIds * @return: com.zy.core.base.BaseOut * @author: xxx * @Date: 2020/9/27 16:18 */ @PostMapping("del") public BaseOut deleteJob(@RequestBody String logIds) { try{ String[] ids = logIds.split(StringPool.COMMA); jobLogService.deleteJobLogs(ids); return BoxOut.build(BDic.SUCCESS,"删除定时任务成功"); }catch (Exception e){ log.error("删除任务日志失败",e.getMessage()); return BoxOut.build(BDic.FAIL, "删除任务日志失败"); } } }
Service:
package com.zy.quartz.service; import com.baomidou.mybatisplus.plugins.Page; import com.zy.core.base.ZyBaseService; import com.zy.quartz.mapper.JobMapper; import com.zy.quartz.model.Job; import com.zy.quartz.model.in.JobIn; import com.zy.quartz.utils.ScheduleUtils; import jodd.util.StringPool; import lombok.extern.slf4j.Slf4j; import org.quartz.Scheduler; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.annotation.PostConstruct; import java.util.Arrays; import java.util.Date; import java.util.List; /** * @description:任务调度服务 * @return: * @author: xxx * @Date: 2020/9/30 15:20 */ @Slf4j @Service("JobService") @Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) public class JobService extends ZyBaseService<Job, JobMapper> { @Autowired private Scheduler scheduler; @Autowired private JobMapper jobMapper; /** * @description:项目启动时,初始化定时任务 * @return: void * @author: xxx * @Date: 2020/9/27 16:45 */ @PostConstruct public void init() { // List<Job> scheduleJobList = jobMapper.queryList(new JobIn()); // // 如果不存在,则创建 // scheduleJobList.forEach(scheduleJob -> { // CronTrigger cronTrigger = ScheduleUtils.getCronTrigger(scheduler, scheduleJob.getJobId()); // if (cronTrigger == null) { // ScheduleUtils.createScheduleJob(scheduler, scheduleJob); // } else { // ScheduleUtils.updateScheduleJob(scheduler, scheduleJob); // } // }); } /** * @description:根据ID获取任务 * @param jobId * @return: com.zy.game.model.Job * @author: xxx * @Date: 2020/9/27 16:44 */ public Job findJob(Long jobId) { return jobMapper.getById(jobId); } /** * @description:获取定时任务 * @param job * @return: com.baomidou.mybatisplus.plugins.Page<com.zy.game.model.Job> * @author: xxx * @Date: 2020/9/27 16:45 */ public Page<Job> findJobs(JobIn job) { Page<Job> page = getPageBeanBPI(job,Job.class); page.setOpenSort(false); List<Job> jobList = jobMapper.queryList(page,job); page.setRecords(jobList); return page; } /** * @description:创建定时任务 * @param job * @return: void * @author: xxx * @Date: 2020/9/27 16:45 */ @Transactional public void createJob(Job job) { job.setCreateTime(new Date()); job.setStatus(Job.ScheduleStatus.PAUSE.getValue()); jobMapper.insert(job); ScheduleUtils.createScheduleJob(scheduler, job); } /** * @description:修改定时任务 * @return: void * @author: xxx * @Date: 2020/9/27 16:46 */ @Transactional public void updateJob(JobIn jobIn) { Job job = new Job(); BeanUtils.copyProperties(jobIn,job); ScheduleUtils.updateScheduleJob(scheduler, job); jobMapper.updateById(job); } /** * @description:删除定时任务 * @param jobIds * @return: void * @author: xxx * @Date: 2020/9/27 16:46 */ @Transactional public void deleteJobs(String[] jobIds) { List<String> list = Arrays.asList(jobIds); list.forEach(jobId -> ScheduleUtils.deleteScheduleJob(scheduler, Long.valueOf(jobId))); jobMapper.deleteBatchIds(list); } /** * @description:暂停定时任务 * @param jobIds * @param status * @return: int * @author: xxx * @Date: 2020/9/27 16:46 */ @Transactional public void updateBatch(String jobIds, String status) { List<String> list = Arrays.asList(jobIds.split(StringPool.COMMA)); list.forEach(s -> { Job job = new Job(); job.setStatus(status); job.setJobId(Long.valueOf(s)); jobMapper.updateById(job); }); } /** * @description:执行定时任务 * @param jobIds * @return: void * @author: xxx * @Date: 2020/9/27 16:46 */ @Transactional public void run(String jobIds) { String[] list = jobIds.split(StringPool.COMMA); Arrays.stream(list).forEach(jobId -> ScheduleUtils.run(scheduler, this.findJob(Long.valueOf(jobId)))); } /** * @description: 暂停定时任务 * @param jobIds * @return: void * @author: xxx * @Date: 2020/9/27 16:46 */ @Transactional public void pause(String jobIds) { String[] list = jobIds.split(StringPool.COMMA); Arrays.stream(list).forEach(jobId -> ScheduleUtils.pauseJob(scheduler, Long.valueOf(jobId))); this.updateBatch(jobIds, Job.ScheduleStatus.PAUSE.getValue()); } /** * @description:恢复定时任务 * @param jobIds * @return: void * @author: xxx * @Date: 2020/9/27 16:47 */ @Transactional public void resume(String jobIds) { String[] list = jobIds.split(StringPool.COMMA); Arrays.stream(list).forEach(jobId -> ScheduleUtils.resumeJob(scheduler, Long.valueOf(jobId))); this.updateBatch(jobIds, Job.ScheduleStatus.NORMAL.getValue()); } } package com.zy.quartz.service; import com.baomidou.mybatisplus.plugins.Page; import com.zy.core.base.ZyBaseService; import com.zy.quartz.mapper.JobLogMapper; import com.zy.quartz.model.JobLog; import com.zy.quartz.model.in.JobLogIn; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.util.Arrays; import java.util.List; /** * @description:任务日志服务 * @return: * @author: xxx * @Date: 2020/9/30 15:20 */ @Slf4j @Service("JobLogService") @Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) public class JobLogService extends ZyBaseService<JobLog, JobLogMapper> { @Autowired private JobLogMapper jobLogMapper; public Page<JobLog> findJobLogs(JobLogIn jobLogIn) { Page<JobLog> page = getPageBeanBPI(jobLogIn,JobLog.class); page.setOpenSort(false); List<JobLog> jobList = jobLogMapper.queryList(page,jobLogIn); page.setRecords(jobList); return page; } @Transactional public void saveJobLog(JobLog log) { jobLogMapper.insert(log); } @Transactional public void deleteJobLogs(String[] jobLogIds) { List<String> list = Arrays.asList(jobLogIds); jobLogMapper.deleteBatchIds(list); } }
Mapper:
package com.zy.quartz.mapper; import com.zy.core.base.ZyBaseMapper; import com.zy.quartz.model.Job; import com.zy.quartz.model.in.JobIn; import com.zy.quartz.model.out.JobOut; import org.apache.ibatis.session.RowBounds; import org.springframework.stereotype.Component; import java.util.List; /** * @author MrBird */ @Component public interface JobMapper extends ZyBaseMapper<Job> { List<JobOut> queryList(RowBounds rowBounds, JobIn jobIn); Job getById(Long jobId); } package com.zy.quartz.mapper; import com.zy.core.base.ZyBaseMapper; import com.zy.quartz.model.JobLog; import com.zy.quartz.model.in.JobLogIn; import org.apache.ibatis.session.RowBounds; import org.springframework.stereotype.Component; import java.util.List; /** * @author MrBird */ @Component public interface JobLogMapper extends ZyBaseMapper<JobLog> { List<JobLog> queryList(RowBounds rowBounds, JobLogIn jobLogIn); }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.zy.quartz.mapper.JobMapper"> <select id="queryList" resultType="com.zy.quartz.model.out.JobOut" parameterType="com.zy.quartz.model.in.JobIn"> select t.job_id as jobId, t.bean_name as beanName, t.method_name as methodName, t.params, t.cron_expression as cronExpression, t.status, t.remark, t.create_time as createTime from zy_job as t where t.enable = 'Y' <if test="beanName!=null and beanName !=''"> and t.bean_name like concat('%', #{beanName}, '%') </if> <if test="methodName!=null and methodName !=''"> and t.method_name like concat('%', #{methodName}, '%') </if> <if test="cronExpression!=null and cronExpression !=''"> and t.cron_expression = #{cronExpression} </if> <if test="status!=null and status !=''"> and t.status = #{status} </if> <if test="remark!=null and remark !=''"> and t.remark like concat('%', #{remark}, '%') </if> <if test="beginTime != null and beginTime != ''"> and DATE_FORMAT(t.create_time,'%Y-%m-%d') <![CDATA[ >= ]]> #{beginTime} </if> <if test="endTime != null and endTime != ''"> and DATE_FORMAT(t.create_time,'%Y-%m-%d') <![CDATA[ <= ]]> #{endTime} </if> order by t.create_time desc,t.job_id asc </select> <select id="getById" resultType="com.zy.quartz.model.Job" parameterType="java.lang.Long"> select job_id as jobId, bean_name as beanName, method_name as methodName, params, cron_expression as cronExpression, status, remark, create_time as createTime from zy_job where enable = 'Y' and job_id = #{jobId} </select> </mapper> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.zy.quartz.mapper.JobLogMapper"> <select id="queryList" resultType="com.zy.quartz.model.JobLog" parameterType="com.zy.quartz.model.in.JobLogIn"> select t.log_id as logId, t.job_id as jobId, t.bean_name as beanName, t.method_name as methodName, t.params, t.status, t.error, t.times, t.create_time as createTime from zy_job_log t where t.log_id = t.log_id <if test="jobId!=null"> and t.job_id = #{jobId} </if> <if test="beanName!=null and beanName !=''"> and t.bean_name like concat('%', #{beanName}, '%') </if> <if test="methodName!=null and methodName !=''"> and t.method_name like concat('%', #{methodName}, '%') </if> <if test="error!=null and error !=''"> and t.error like concat('%', #{error}, '%') </if> <if test="status!=null and status !=''"> and t.status = #{status} </if> <if test="beginTime != null and beginTime != ''"> and DATE_FORMAT(t.create_time,'%Y-%m-%d') <![CDATA[ >= ]]> #{beginTime} </if> <if test="endTime != null and endTime != ''"> and DATE_FORMAT(t.create_time,'%Y-%m-%d') <![CDATA[ <= ]]> #{endTime} </if> order by t.create_time desc </select> </mapper>
Model:
package com.zy.quartz.model.in; import com.zy.core.base.BasePageIn; import com.zy.quartz.model.Job; import lombok.Data; import java.util.Date; /** * @author zxq * @description 定时任务入参实体 * @date 2020/9/27 */ @Data public class JobIn extends BasePageIn { //定时任务ID字符串,逗号间隔 private String jobIds; //定时任务ID private Long jobId; //spring bean名 private String beanName; //方法名 private String methodName; //参数 private String params; //cron表达式 private String cronExpression; //状态;0=正常,1=暂停 private String status; //备注 private String remark; //是否可用 默认为Y (Y:是,N:否) private String enable; //创建人 private String createBy; //开始时间 private transient String beginTime; //结束时间 private transient String endTime; } package com.zy.quartz.model.in; import com.zy.core.base.BasePageIn; import lombok.Data; /** * @author zxq * @description 任务日志 * @date 2020/9/30 */ @Data public class JobLogIn extends BasePageIn { //日志ID字符串,逗号间隔 private String logIds; //任务ID private Long jobId; //spring bean名称 private String beanName; //方法名 private String methodName; //参数 private String params; //失败信息 private String error; //状态;0=正常,1=暂停 private String status; //耗时(单位:毫秒) private String times; //开始时间 private transient String beginTime; //结束时间 private transient String endTime; } package com.zy.quartz.model.out; import com.zy.quartz.model.Job; import lombok.Data; import java.util.Date; /** * @author zxq * @description 定时任务出参实体 * @date 2020/9/27 */ @Data public class JobOut { private String jobIds; private Long jobId; private String beanName; private String methodName; private String params; private String cronExpression; private String status; private String remark; private Date createTime; private String enable; private String createBy; } package com.zy.quartz.model; import com.baomidou.mybatisplus.annotations.TableField; import com.baomidou.mybatisplus.annotations.TableId; import com.baomidou.mybatisplus.annotations.TableName; import com.baomidou.mybatisplus.enums.IdType; import lombok.Data; import javax.validation.constraints.Size; import java.io.Serializable; import java.util.Date; /** * @description:定时任务信息表 * @return: * @author: zxq * @Date: 2020/9/27 10:49 */ @Data @TableName("zy_job") public class Job implements Serializable { private static final long serialVersionUID = 400066840871805700L; /** * 任务调度参数 key */ public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY"; public enum ScheduleStatus { /** * 正常 */ NORMAL("0"), /** * 暂停 */ PAUSE("1"); private String value; ScheduleStatus(String value) { this.value = value; } public String getValue() { return value; } } @TableId(value = "job_id", type = IdType.AUTO) private Long jobId; @TableField("bean_name") private String beanName; @TableField("method_name") private String methodName; @TableField("params") private String params; @TableField("cron_expression") private String cronExpression; @TableField("status") private String status; @TableField("remark") private String remark; @TableField("create_time") private Date createTime; /** * 是否可用 默认为Y (Y:是,N:否) */ @TableField("enable") private String enable; /** * */ @TableField("create_by") private String createBy; } package com.zy.quartz.model; import com.baomidou.mybatisplus.annotations.TableField; import com.baomidou.mybatisplus.annotations.TableId; import com.baomidou.mybatisplus.annotations.TableName; import com.baomidou.mybatisplus.enums.IdType; import com.zy.core.base.BasePageIn; import lombok.Data; import java.io.Serializable; import java.util.Date; /** * @description:调度日志信息表 * @return: * @author: zxq * @Date: 2020/9/27 10:49 */ @Data @TableName("zy_job_log") public class JobLog implements Serializable { private static final long serialVersionUID = -7114915445674333148L; // 任务执行成功 public static final String JOB_SUCCESS = "0"; // 任务执行失败 public static final String JOB_FAIL = "1"; @TableId(value = "log_id", type = IdType.AUTO) private Long logId; @TableField("job_id") private Long jobId; @TableField("bean_name") private String beanName; @TableField("method_name") private String methodName; @TableField("params") private String params; @TableField("status") private String status; @TableField("error") private String error; @TableField("times") private Long times; @TableField("create_time") private Date createTime; }
Utils:
package com.zy.quartz.utils; import com.zy.quartz.model.Job; import com.zy.quartz.model.JobLog; import com.zy.quartz.service.JobLogService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.quartz.JobExecutionContext; import org.springframework.scheduling.quartz.QuartzJobBean; import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * 定时任务 * * @author MrBird */ @Slf4j public class ScheduleJob extends QuartzJobBean { private ExecutorService service = Executors.newSingleThreadExecutor(); @Override protected void executeInternal(JobExecutionContext context) { Job scheduleJob = (Job) context.getMergedJobDataMap().get(Job.JOB_PARAM_KEY); // 获取spring bean JobLogService scheduleJobLogService = SpringContextUtil.getBean(JobLogService.class); JobLog jobLog = new JobLog(); jobLog.setJobId(scheduleJob.getJobId()); jobLog.setBeanName(scheduleJob.getBeanName()); jobLog.setMethodName(scheduleJob.getMethodName()); jobLog.setParams(scheduleJob.getParams()); jobLog.setCreateTime(new Date()); long startTime = System.currentTimeMillis(); try { // 执行任务 log.info("任务准备执行,任务ID:{}", scheduleJob.getJobId()); ScheduleRunnable task = new ScheduleRunnable(scheduleJob.getBeanName(), scheduleJob.getMethodName(), scheduleJob.getParams()); Future<?> future = service.submit(task); future.get(); long times = System.currentTimeMillis() - startTime; jobLog.setTimes(times); // 任务状态 0:成功 1:失败 jobLog.setStatus(JobLog.JOB_SUCCESS); log.info("任务执行完毕,任务ID:{} 总共耗时:{} 毫秒", scheduleJob.getJobId(), times); } catch (Exception e) { log.error("任务执行失败,任务ID:" + scheduleJob.getJobId(), e); long times = System.currentTimeMillis() - startTime; jobLog.setTimes(times); // 任务状态 0:成功 1:失败 jobLog.setStatus(JobLog.JOB_FAIL); jobLog.setError(StringUtils.substring(e.toString(), 0, 2000)); } finally { scheduleJobLogService.saveJobLog(jobLog); } } } package com.zy.quartz.utils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.util.ReflectionUtils; import java.lang.reflect.Method; /** * 执行定时任务 * * @author MrBird */ @Slf4j public class ScheduleRunnable implements Runnable { private Object target; private Method method; private String params; ScheduleRunnable(String beanName, String methodName, String params) throws NoSuchMethodException, SecurityException { this.target = SpringContextUtil.getBean(beanName); this.params = params; if (StringUtils.isNotBlank(params)) { this.method = target.getClass().getDeclaredMethod(methodName, String.class); } else { this.method = target.getClass().getDeclaredMethod(methodName); } } @Override public void run() { try { ReflectionUtils.makeAccessible(method); if (StringUtils.isNotBlank(params)) { method.invoke(target, params); } else { method.invoke(target); } } catch (Exception e) { log.error("执行定时任务失败", e); } } } package com.zy.quartz.utils; import lombok.extern.slf4j.Slf4j; import org.quartz.*; import com.zy.quartz.model.Job; /** * 定时任务工具类 * * @author MrBird */ @Slf4j public class ScheduleUtils { protected ScheduleUtils() { } private static final String JOB_NAME_PREFIX = "TASK_"; /** * 获取触发器 key */ private static TriggerKey getTriggerKey(Long jobId) { return TriggerKey.triggerKey(JOB_NAME_PREFIX + jobId); } /** * 获取jobKey */ private static JobKey getJobKey(Long jobId) { return JobKey.jobKey(JOB_NAME_PREFIX + jobId); } /** * 获取表达式触发器 */ public static CronTrigger getCronTrigger(Scheduler scheduler, Long jobId) { try { return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId)); } catch (SchedulerException e) { log.error("获取Cron表达式失败", e); } return null; } /** * 创建定时任务 */ public static void createScheduleJob(Scheduler scheduler, Job scheduleJob) { try { // 构建job信息 JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(scheduleJob.getJobId())) .build(); // 表达式调度构建器 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()) .withMisfireHandlingInstructionDoNothing(); // 按新的cronExpression表达式构建一个新的trigger CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJob.getJobId())) .withSchedule(scheduleBuilder).build(); // 放入参数,运行时的方法可以获取 jobDetail.getJobDataMap().put(Job.JOB_PARAM_KEY, scheduleJob); scheduler.scheduleJob(jobDetail, trigger); // 暂停任务 if (scheduleJob.getStatus().equals(Job.ScheduleStatus.PAUSE.getValue())) { pauseJob(scheduler, scheduleJob.getJobId()); } } catch (SchedulerException e) { log.error("创建定时任务失败", e); } } /** * 更新定时任务 */ public static void updateScheduleJob(Scheduler scheduler, Job scheduleJob) { try { TriggerKey triggerKey = getTriggerKey(scheduleJob.getJobId()); // 表达式调度构建器 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()) .withMisfireHandlingInstructionDoNothing(); CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getJobId()); if (trigger == null) { throw new SchedulerException("获取Cron表达式失败"); } else { // 按新的 cronExpression表达式重新构建 trigger trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); // 参数 trigger.getJobDataMap().put(Job.JOB_PARAM_KEY, scheduleJob); } scheduler.rescheduleJob(triggerKey, trigger); // 暂停任务 if (scheduleJob.getStatus().equals(Job.ScheduleStatus.PAUSE.getValue())) { pauseJob(scheduler, scheduleJob.getJobId()); } } catch (SchedulerException e) { log.error("更新定时任务失败", e); } } /** * 立即执行任务 */ public static void run(Scheduler scheduler, Job scheduleJob) { try { // 参数 JobDataMap dataMap = new JobDataMap(); dataMap.put(Job.JOB_PARAM_KEY, scheduleJob); scheduler.triggerJob(getJobKey(scheduleJob.getJobId()), dataMap); } catch (SchedulerException e) { log.error("执行定时任务失败", e); } } /** * 暂停任务 */ public static void pauseJob(Scheduler scheduler, Long jobId) { try { scheduler.pauseJob(getJobKey(jobId)); } catch (SchedulerException e) { log.error("暂停定时任务失败", e); } } /** * 恢复任务 */ public static void resumeJob(Scheduler scheduler, Long jobId) { try { scheduler.resumeJob(getJobKey(jobId)); } catch (SchedulerException e) { log.error("恢复定时任务失败", e); } } /** * 删除定时任务 */ public static void deleteScheduleJob(Scheduler scheduler, Long jobId) { try { scheduler.deleteJob(getJobKey(jobId)); } catch (SchedulerException e) { log.error("删除定时任务失败", e); } } } package com.zy.quartz.utils; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** * @description:Spring Context 工具类 * @return: * @author: zxq * @Date: 2020/9/27 11:07 */ @Component public class SpringContextUtil implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringContextUtil.applicationContext = applicationContext; } public static Object getBean(String name) { return applicationContext.getBean(name); } public static <T> T getBean(Class<T> clazz){ return applicationContext.getBean(clazz); } public static <T> T getBean(String name, Class<T> requiredType) { return applicationContext.getBean(name, requiredType); } public static boolean containsBean(String name) { return applicationContext.containsBean(name); } public static boolean isSingleton(String name) { return applicationContext.isSingleton(name); } public static Class<?> getType(String name) { return applicationContext.getType(name); } }
SQL:
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS; DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS; DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE; DROP TABLE IF EXISTS QRTZ_LOCKS; DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS; DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS; DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS; DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS; DROP TABLE IF EXISTS QRTZ_TRIGGERS; DROP TABLE IF EXISTS QRTZ_JOB_DETAILS; DROP TABLE IF EXISTS QRTZ_CALENDARS; CREATE TABLE QRTZ_JOB_DETAILS ( SCHED_NAME VARCHAR(120) NOT NULL, JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) NOT NULL, DESCRIPTION VARCHAR(250) NULL, JOB_CLASS_NAME VARCHAR(250) NOT NULL, IS_DURABLE VARCHAR(1) NOT NULL, IS_NONCONCURRENT VARCHAR(1) NOT NULL, IS_UPDATE_DATA VARCHAR(1) NOT NULL, REQUESTS_RECOVERY VARCHAR(1) NOT NULL, JOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) ); CREATE TABLE QRTZ_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) NOT NULL, DESCRIPTION VARCHAR(250) NULL, NEXT_FIRE_TIME BIGINT(13) NULL, PREV_FIRE_TIME BIGINT(13) NULL, PRIORITY INTEGER NULL, TRIGGER_STATE VARCHAR(16) NOT NULL, TRIGGER_TYPE VARCHAR(8) NOT NULL, START_TIME BIGINT(13) NOT NULL, END_TIME BIGINT(13) NULL, CALENDAR_NAME VARCHAR(200) NULL, MISFIRE_INSTR SMALLINT(2) NULL, JOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP) ); CREATE TABLE QRTZ_SIMPLE_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, REPEAT_COUNT BIGINT(7) NOT NULL, REPEAT_INTERVAL BIGINT(12) NOT NULL, TIMES_TRIGGERED BIGINT(10) NOT NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_CRON_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, CRON_EXPRESSION VARCHAR(200) NOT NULL, TIME_ZONE_ID VARCHAR(80), PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_SIMPROP_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, STR_PROP_1 VARCHAR(512) NULL, STR_PROP_2 VARCHAR(512) NULL, STR_PROP_3 VARCHAR(512) NULL, INT_PROP_1 INT NULL, INT_PROP_2 INT NULL, LONG_PROP_1 BIGINT NULL, LONG_PROP_2 BIGINT NULL, DEC_PROP_1 NUMERIC(13,4) NULL, DEC_PROP_2 NUMERIC(13,4) NULL, BOOL_PROP_1 VARCHAR(1) NULL, BOOL_PROP_2 VARCHAR(1) NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_BLOB_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, BLOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_CALENDARS ( SCHED_NAME VARCHAR(120) NOT NULL, CALENDAR_NAME VARCHAR(200) NOT NULL, CALENDAR BLOB NOT NULL, PRIMARY KEY (SCHED_NAME,CALENDAR_NAME) ); CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_FIRED_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, ENTRY_ID VARCHAR(95) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, INSTANCE_NAME VARCHAR(200) NOT NULL, FIRED_TIME BIGINT(13) NOT NULL, SCHED_TIME BIGINT(13) NOT NULL, PRIORITY INTEGER NOT NULL, STATE VARCHAR(16) NOT NULL, JOB_NAME VARCHAR(200) NULL, JOB_GROUP VARCHAR(200) NULL, IS_NONCONCURRENT VARCHAR(1) NULL, REQUESTS_RECOVERY VARCHAR(1) NULL, PRIMARY KEY (SCHED_NAME,ENTRY_ID) ); CREATE TABLE QRTZ_SCHEDULER_STATE ( SCHED_NAME VARCHAR(120) NOT NULL, INSTANCE_NAME VARCHAR(200) NOT NULL, LAST_CHECKIN_TIME BIGINT(13) NOT NULL, CHECKIN_INTERVAL BIGINT(13) NOT NULL, PRIMARY KEY (SCHED_NAME,INSTANCE_NAME) ); CREATE TABLE QRTZ_LOCKS ( SCHED_NAME VARCHAR(120) NOT NULL, LOCK_NAME VARCHAR(40) NOT NULL, PRIMARY KEY (SCHED_NAME,LOCK_NAME) ); SET FOREIGN_KEY_CHECKS=0;
效果图: