场景:
需求:
支付的二维码,超过两个小时以后,如果还未支付,则自动转为取消支付,或者支付超时的状态
需求分析:
1,动态定时任务:
每个支付的二维码创建的时候,创建一个动态的定时任务,两个小时候自动执行,更新支付状态,可以解决这个问题。
(1)持久化:
如果服务重启了,动态定时任务会丢失,导致部分数据没办法更新状态。
(2)分布式:
如果当服务重启时,自动扫描数据,重新计算时间,再次创建动态定时任务。可以解决(1)的问题,但是当分布式,多个节点的时候,都会重新加载所有的任务,这样性能上不是最优解,只能在数据源上加上节点名称,不同的服务节点,加载属于自己的定时任务,可以解决这个问题。
虽然有那么多问题,还是封装一下,万一以后有用得到的时候呢
实现
1,创建Scheduler对象
2,基础对象,其中三个熟悉都是必须要用的,方便后期开发,仅需继承该类就可以了。
3,创建动态定时任务工具类。
4,测试
实现
创建bean
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 创建scheduler对象
*
* @module
* @author frank
* @date 2021/8/21 11:51
*/
@Configuration
public class SchedulerConfig{
/**
* 初始注入scheduler
*
* @return
* @throws SchedulerException
*/
@Bean
public Scheduler scheduler() throws SchedulerException {
SchedulerFactory schedulerFactoryBean = new StdSchedulerFactory();
return schedulerFactoryBean.getScheduler();
}
}
创建实体类父类
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.UUID;
/**
* 创建动态定时任务基础父类
*
* @module
* @author frank
* @date 2021/8/21 11:51
*/
@Data
public class SchedulerBaseVo implements Serializable {
/**
* 任务名称-唯一
*/
@ApiModelProperty(value = "任务名称")
private String jobName;
/**
* 触发器名称-唯一
*/
@ApiModelProperty(value = "触发器名称")
private String triggerName;
/**
* 分组名称-唯一
*/
@ApiModelProperty(value = "分组名称")
private String groupName;
public SchedulerBaseVo() {
this.groupName = "notSchool";
this.jobName = UUID.randomUUID().toString();
this.triggerName = UUID.randomUUID().toString();
}
public SchedulerBaseVo(String jobName) {
this.jobName = jobName;
this.triggerName = jobName;
this.groupName = "notSchool";
}
public SchedulerBaseVo(String jobName, String groupName) {
this.jobName = jobName;
this.triggerName = jobName;
this.groupName = groupName;
}
public SchedulerBaseVo(String jobName, String triggerName, String groupName) {
this.jobName = jobName;
this.triggerName = triggerName;
this.groupName = groupName;
}
public void init(String jobName,String groupName) {
this.jobName = jobName;
this.triggerName = jobName;
this.groupName = groupName;
}
}
创建动态定时任务工具类
import com.sxmaps.netschool.common.utils.DateUtil;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
/**
* 创建动态定时任务
*
* @module
* @author frank
* @date 2021/8/21 11:50
*/
@Component
public class DynamicScheduledTask<T extends SchedulerBaseVo> {
private final Logger logger = LoggerFactory.getLogger(DynamicScheduledTask.class);
@Autowired
Scheduler scheduler;
/**
* 创建并执行任务
*/
private void createScheduler(T t, Date tsTime, String jobName, String triggerName, String groupName, int intervalInSeconds, Class<? extends Job> clazz) throws SchedulerException {
logger.info("添加动态定时任务,任务名称:{},触发器名称:{},分组名称:{},触发间隔:{},执行时间:{},内容:{}" , jobName, triggerName,groupName,intervalInSeconds,tsTime,t);
JobDetail job = JobBuilder.newJob(clazz).withIdentity(jobName, groupName).build();
job.getJobDataMap().put(t.getClass().getName(), t);
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerName, groupName)
.startAt(tsTime)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(intervalInSeconds).withRepeatCount(0))
.build();
// 创建实例
try {
scheduler.scheduleJob(job, trigger);
if (!scheduler.isShutdown()) {
scheduler.start();
}
} catch (SchedulerException e) {
scheduler.shutdown(true);
logger.error("创建动态定时任务失败:",e);
}
}
/**
* 创建并执行任务
*/
public void createSchedulerHour(T t, Integer delay, Class<? extends Job> clazz) {
try {
createScheduler(t, DateUtil.addHour(DateUtil.currentDate(),delay), t.getJobName(), t.getTriggerName(), t.getGroupName(), 10,clazz);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* 创建并执行任务
*/
public void createSchedulerSecond(T t, Integer delay, Class<? extends Job> clazz) {
try {
createScheduler(t, DateUtil.addSecond(DateUtil.currentDate(),delay), t.getJobName(), t.getTriggerName(), t.getGroupName(), 10,clazz);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* 删除任务
*/
public boolean deleteJob(String jobName, String groupName) {
try {
return scheduler.deleteJob(JobKey.jobKey(jobName, groupName));
} catch (SchedulerException e) {
}
return Boolean.FALSE;
}
//--
public void getAllJobs() throws SchedulerException {
for (String groupName : scheduler.getJobGroupNames()) {
for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName))) {
String jobName = jobKey.getName();
String jobGroup = jobKey.getGroup();
List<Trigger> triggers = (List<Trigger>) scheduler.getTriggersOfJob(jobKey);
Date nextFireTime = triggers.get(0).getNextFireTime();
logger.debug("========== jobName: {}, groupName: {}, nextTime: {}", jobName, jobGroup, nextFireTime);
}
}
}
}
动态定时任务执行
import com.sxmaps.netschool.common.utils.SpringContextUtil;
import com.sxmaps.netschool.service.SchoolAccountService;
import com.sxmaps.netschool.service.dto.school.SchoolAccountPayStateRespDTO;
import com.sxmaps.netschool.service.vo.school.SchoolAccountPayStateReqVO;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* 定时任务执行类
*
* @author frank
* @module
* @date 2021/8/19 10:49
*/
@Component
public class PayQCordJob implements Job {
private final Logger logger = LoggerFactory.getLogger(PayQCordJob.class);
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
SchoolAccountPayStateReqVO payStateReqVO = (SchoolAccountPayStateReqVO) context.getJobDetail().getJobDataMap().get(SchoolAccountPayStateReqVO.class.getName());
logger.info("支付二维码-定时失效,内容:{}", payStateReqVO);
SchoolAccountService schoolAccountService = (SchoolAccountService) SpringContextUtil.getBeanByClass(SchoolAccountService.class);
SchoolAccountPayStateRespDTO respDTO = schoolAccountService.payState(payStateReqVO);
logger.info("支付二维码-定时失效,内容:{},处理结果:{}", payStateReqVO, respDTO);
}
}
测试
@Autowired
DynamicScheduledTask scheduledTask;
......
scheduledTask.createSchedulerSecond(new SchoolAccountPayStateReqVO(dto.getPayNo()),30, PayQCordJob.class);