目录
- 概述
- 示例
- jar包
- 配置类
- 任务详情
- 项目应用
- 封装的工具类QuartzUtils
- 封装IQuartzSrvice和QuartzServiceImpl
- 封装参数QuartzJobInfo
- 编写任务逻辑MainJob
- 调用第三方支付前添加定时任务
- 异步回调后移除定时任务
- 订单支付整体流程
概述
优势:Tmer不支持持久化,重启需要重新安排,Quartz可以持久化,满足大部分需要
缺点:高并发情况下,大量定时任务存在会影响性能。
jvm默认大小500m,定时任务对象太多会内存溢出宕机,该方案只能在1000并发以下比较适合。正常来说我们就满足,只有秒杀,抢单等。。。
高并发情况下要换用消息队列。
原理图
示例
jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
配置类
添加定时任务的配置类 - 交给spring容器管理就自动会跑起来
@Configuration
public class QuartzConfig {
@Bean
public JobDetail newJob(){
return JobBuilder.newJob(PrintTimeJob.class)//PrintTimeJob我们的业务类
.withIdentity("9527")//可以给该JobDetail起一个id
//每个JobDetail内都有一个Map,包含了关联到这个Job的数据,在Job类中可以通过context获取
.usingJobData("msg", "Hello Quartz")//关联键值对
.storeDurably()//即使没有Trigger关联时,也不需要删除该JobDetail
.build();
}
@Bean
public Trigger printTimeJobTrigger() {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/1 * * * * ? ");
return TriggerBuilder.newTrigger()
.forJob(newJob())//关联上述的JobDetail
.withIdentity("quartzTaskService")//给Trigger起个名字
.withSchedule(cronScheduleBuilder)
.build();
}
}
任务详情
/**
* 定时任务业务处理类,我们继承QuartzJobBean
* 重写executeInternal方法来实现具体的定时业务逻辑
*/
public class PrintTimeJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
//获取JobDetail中关联的数据
String msg = (String) context.getJobDetail().getJobDataMap().get("msg");
System.out.println("current time :"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "---" + msg);
}
}
项目应用
封装的工具类QuartzUtils
import org.quartz.*;
import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;
/**
* Quartz调度管理器
*/
public class QuartzUtils {
private static String JOB_GROUP_NAME = "JOB_GROUP_SYSTEM";
private static String TRIGGER_GROUP_NAME = "TRIGGER_GROUP_SYSTEM";
/**
* @Description: 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名
* @param sched 调度器
* @param jobName 任务名
* @param cls 任务
* @param params 任务参数
* @param time 时间设置,参考quartz说明文档
* @Title: QuartzManager.java
*/
public static void addJob(Scheduler sched, String jobName, @SuppressWarnings("rawtypes") Class cls, Object params,
String time) {
try {
JobKey jobKey = new JobKey(jobName, JOB_GROUP_NAME);// 任务名,任务组,任务执行类
@SuppressWarnings("unchecked")
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("params", params);
JobDetail jobDetail = newJob(cls).withIdentity(jobKey).setJobData(jobDataMap).build();
TriggerKey triggerKey = new TriggerKey(jobName, TRIGGER_GROUP_NAME);// 触发器
Trigger trigger = newTrigger().withIdentity(triggerKey).withSchedule(cronSchedule(time)).build();// 触发器时间设定
sched.scheduleJob(jobDetail, trigger);
if (!sched.isShutdown()) {
sched.start();// 启动
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 添加一个定时任务
* @param sched 调度器
* @param jobName 任务名
* @param jobGroupName 任务组名
* @param triggerName 触发器名
* @param triggerGroupName 触发器组名
* @param jobClass 任务
* @param params 任务参数
* @param time 时间设置,参考quartz说明文档
* @Title: QuartzManager.java
*/
public static void addJob(Scheduler sched, String jobName, String jobGroupName, String triggerName,
String triggerGroupName, @SuppressWarnings("rawtypes") Class jobClass, Object params, String time) {
try {
JobKey jobKey = new JobKey(jobName, jobGroupName);
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("params", params);
@SuppressWarnings("unchecked")
JobDetail jobDetail = newJob(jobClass).withIdentity(jobKey).setJobData(jobDataMap).build();
// 触发器
TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroupName);
Trigger trigger = newTrigger().withIdentity(triggerKey).withSchedule(cronSchedule(time)).build();
sched.scheduleJob(jobDetail, trigger);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 修改一个任务的触发时间(使用默认的任务组名,触发器名,触发器组名)
* @param sched 调度器
* @param jobName
* @param time
* @Title: QuartzManager.java
*/
@SuppressWarnings("rawtypes")
public static void modifyJobTime(Scheduler sched, String jobName, String time) {
try {
TriggerKey triggerKey = new TriggerKey(jobName, TRIGGER_GROUP_NAME);
CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
if (trigger == null) {
return;
}
String oldTime = trigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(time)) {
JobKey jobKey = new JobKey(jobName, JOB_GROUP_NAME);
JobDetail jobDetail = sched.getJobDetail(jobKey);
Class objJobClass = jobDetail.getJobClass();
Object params = jobDetail.getJobDataMap().get("params");
removeJob(sched, jobName);
System.out.println("修改任务:" + jobName);
addJob(sched, jobName, objJobClass, params,time);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 修改一个任务的触发时间
* @param sched 调度器
* @param sched 调度器
* @param triggerName
* @param triggerGroupName
* @param time
* @Title: QuartzManager.java
*/
public static void modifyJobTime(Scheduler sched, String triggerName, String triggerGroupName, String time) {
try {
TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroupName);
CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
if (trigger == null) {
return;
}
String oldTime = trigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(time)) {
// 修改时间
trigger.getTriggerBuilder().withSchedule(cronSchedule(time));
// 重启触发器
sched.resumeTrigger(triggerKey);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 移除一个任务(使用默认的任务组名,触发器名,触发器组名)
* @param sched 调度器
* @param jobName
* @Title: QuartzManager.java
*/
public static void removeJob(Scheduler sched, String jobName) {
try {
TriggerKey triggerKey = new TriggerKey(jobName, TRIGGER_GROUP_NAME);
sched.pauseTrigger(triggerKey);// 停止触发器
sched.unscheduleJob(triggerKey);// 移除触发器
JobKey jobKey = new JobKey(jobName, JOB_GROUP_NAME);
boolean b = sched.deleteJob(jobKey);// 删除任务
System.out.println(b);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 移除一个任务
* @param sched 调度器
* @param jobName
* @param jobGroupName
* @param triggerName
* @param triggerGroupName
* @Title: QuartzManager.java
*/
public static void removeJob(Scheduler sched, String jobName, String jobGroupName, String triggerName,
String triggerGroupName) {
try {
TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroupName);
sched.pauseTrigger(triggerKey);// 停止触发器
sched.unscheduleJob(triggerKey);// 移除触发器
JobKey jobKey = new JobKey(jobName, jobGroupName);
sched.deleteJob(jobKey);// 删除任务
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description:启动所有定时任务
* @param sched 调度器
* @Title: QuartzManager.java
*/
public static void startJobs(Scheduler sched) {
try {
sched.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description:关闭所有定时任务
* @param sched 调度器
*/
public static void shutdownJobs(Scheduler sched) {
try {
if (!sched.isShutdown()) {
sched.shutdown();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
封装IQuartzSrvice和QuartzServiceImpl
IQuartzService
public interface IQuartzService {
//1.添加定时任务
void addJob(QuartzJobInfo quartzJobInfo);
// 2. 移出定时任务
void removeJob(String jobName);
}
QuartzServiceImpl
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;
@Service
public class QuartzServiceImpl implements IQuartzService {
@Autowired
private SchedulerFactoryBean schedulerFactory;
/*
* @param scheduler
* @param jobName
* @param cls
* @param params
* @param time 时间表达式 定时任务执行的时间 已知 执行的时间 Date--->0/1 * * * * ?
*/
@Override
public void addJob(QuartzJobInfo quartzJobInfo) {
/* Scheduler sched, String jobName, @SuppressWarnings("rawtypes") Class cls, Object params,String time */
try {
QuartzUtils.addJob(schedulerFactory.getScheduler(),quartzJobInfo.getJobName() , MainJob.class,quartzJobInfo.getParams() , quartzJobInfo.getCronj());
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void removeJob(String orderSn) {
QuartzUtils.removeJob(schedulerFactory.getScheduler(),orderSn);
}
}
封装参数QuartzJobInfo
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
@Data
public class QuartzJobInfo implements Serializable {
private byte type;
private String jobName;
private Map<String, Object> params;
private String cronj;
private Date fireDate;
public void setFireDate(Date fireDate) {
this.fireDate = fireDate;
String[] cronArr = new String[7];
for (int i = 0; i < cronArr.length; i++) {
cronArr[i] = "";
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(fireDate);
int second = calendar.get(Calendar.SECOND);
int minute = calendar.get(Calendar.MINUTE);
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int day = calendar.get(Calendar.DAY_OF_MONTH);
int month = calendar.get(Calendar.MONTH) + 1;
int year = calendar.get(Calendar.YEAR);
cronArr[0] = second + "";
cronArr[1] = minute + "";
cronArr[2] = hour + "";
cronArr[3] = day + "";
cronArr[4] = month + "";
cronArr[5] = "?";
cronArr[6] = year + "";
String cron = StringUtils.join(cronArr," ").trim();
this.setCronj(cron);
}
}
编写任务逻辑MainJob
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.Map;
@Component
public class MainJob implements Job {
@Autowired
private IPayBillService payBillService;
@Autowired
private IAdoptOrderService adoptOrderService;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
//获取定时器里面的数据
JobDataMap jobDataMap = context.getMergedJobDataMap();
Map<String,Object> params = (Map<String, Object>) jobDataMap.get("params");
//获取定时任务的订单号
String orderSn = params.get("orderSn").toString();
//执行 修改订单状态的业务逻辑
//1.改订单的状态
PayBill payBill = payBillService.loadByUnionPaySn(orderSn);
payBill.setState(-1);
payBill.setUpdateTime(new Date());
payBillService.update(payBill);
if(PayConstants.BUSINESS_TYPE_ADOPT.equals(payBill.getBusinessType())){//领养订单
Long orderId = payBill.getBusinessKey();
AdoptOrder order = adoptOrderService.getById(orderId);
order.setState(-1);
adoptOrderService.update(order);
}
}
}
调用第三方支付前添加定时任务
AdoptOrderServiceImpl
@Service
public class AdoptOrderServiceImpl extends BaseServiceImpl<AdoptOrder> implements IAdoptOrderService {
@Autowired
private PetMapper petMapper;
@Autowired
private UserMapper userMapper;
@Autowired
private AdoptOrderMapper adoptOrderMapper;
@Autowired
private UserAddressMapper userAddressMapper;
@Autowired
private OrderAddressMapper orderAddressMapper;
@Autowired
private EmployeeMapper employeeMapper;
@Autowired
private PayBillMapper payBillMapper;
@Autowired
private IPayBillService payBillService;
@Autowired
private IQuartzService quartzService;
/**
* 领养订单结算
*/
@Override
@Transactional
public String submit(Map<String, Object> params, Logininfo loginIn) {
Long petId = Long.valueOf(params.get("pet_id").toString());
Long addressId = Long.valueOf(params.get("address_id").toString());//收货地址:t_user_address的id
Integer payMethod = Integer.valueOf(params.get("pay_method").toString());//1支付宝 2微信 3银联 0余额
Integer serviceMethod = Integer.valueOf(params.get("service_method").toString());//送货方式
//1.修改状态 下架
Pet pet = petMapper.loadById(petId);
pet.setState(0);
pet.setOffsaletime(new Date());
//2.绑定用户
User user = userMapper.loadByloingInfoId(loginIn.getId());
pet.setUser(user);
pet.setUser_id(user.getId());
pet.setShop_id(pet.getShop().getId());
petMapper.update(pet);
//3.生成订单 一次性
AdoptOrder order = initAdoptOrder(pet, user);
//骚操作:为了后边操作支付单,不用再来修改订单,先生成统一的支付单号
String unionPaySn = CodeGenerateUtils.generateUnionPaySn();
order.setPaySn(unionPaySn);
adoptOrderMapper.save(order);
// 3.1 生成订单地址
UserAddress userAddress = userAddressMapper.loadById(addressId);
OrderAddress orderAddress = userAddress2OrderAddress(order, userAddress);
orderAddressMapper.save(orderAddress);
// 4.支付单
PayBill payBill = initPayBill(payMethod, pet, user, order);
payBillMapper.save(payBill);
// 5.针对支付单添加定时任务
QuartzJobInfo info = new QuartzJobInfo();
info.setJobName(order.getPaySn());//定时任务的唯一标识
Map<String, Object> map = new HashMap<>();
map.put("orderSn", payBill.getUnionPaySn());
// 定时任务 需要的 数据
info.setParams(map);
info.setFireDate(payBill.getLastPayTime());//最后的支付时间,定时任务的执行时间
quartzService.addJob(info);
//调用统一支付接口(老杨定义的)
return payBillService.pay(payBill);
}
private PayBill initPayBill(Integer payMethod, Pet pet, User user, AdoptOrder order) {
PayBill payBill = new PayBill();
payBill.setDigest(order.getDigest()+"支付单");
payBill.setMoney(order.getPrice());
//重点:支付唯一表示
payBill.setUnionPaySn(order.getPaySn());
payBill.setLastPayTime(new Date(System.currentTimeMillis() + 15*60*1000));
payBill.setPayChannel(Long.valueOf(payMethod));//0 余额 1 支付宝 2 微信 3 银联
payBill.setBusinessType(PayConstants.BUSINESS_TYPE_ADOPT);
payBill.setBusinessKey(order.getId());
payBill.setUser_id(user.getId());
payBill.setShop_id(pet.getShop().getId());
payBill.setNickName(user.getUsername());
return payBill;
}
@Override
public PageList<AdoptOrder> queryAdmin(AdoptOrderQuery query, Long loginInfoId) {
//1.通过loginInfoID查询出Employee
Employee employee = employeeMapper.loadByLoginInfoId(loginInfoId);
//2.如果employee中的shopID不为null,就是店铺。否则就是平台员工
if (employee.getShop_id() != null) {
query.setShopId(employee.getShop_id());
}
return super.queryPage(query);
}
@Override
public PageList<AdoptOrder> queryUser(AdoptOrderQuery query, Long loginInfoId) {
User user = userMapper.loadByloingInfoId(loginInfoId);
query.setUserId(user.getId());
return super.queryPage(query);
}
private OrderAddress userAddress2OrderAddress(AdoptOrder order, UserAddress userAddress) {
OrderAddress orderAddress = new OrderAddress();
BeanUtils.copyProperties(userAddress, orderAddress);
orderAddress.setId(null);
orderAddress.setOrder_id(order.getId());
orderAddress.setOrderSn(order.getOrderSn());
return orderAddress;
}
/**
* alt + shift + m
*
* @param petId
* @param pet
* @param user
* @return
*/
private AdoptOrder initAdoptOrder(Pet pet, User user) {
AdoptOrder order = new AdoptOrder();
order.setDigest("【摘要】" + pet.getName());
order.setPrice(pet.getSaleprice());
order.setOrderSn(CodeGenerateUtils.generateOrderSn(user.getId()));
order.setLastConfirmTime(new Date(System.currentTimeMillis() + PayConstants.LAST_TIME));//最后确认时间
order.setPet_id(pet.getId());
order.setUser_id(user.getId());
order.setShop_id(pet.getShop().getId());
return order;
}
}
异步回调后移除定时任务
AlipayController
@RestController
public class AlipayController {
@Autowired
private IPayBillService payBillService;
@Autowired
private IAlipayInfoService alipayInfoService;
@Autowired
private IAdoptOrderService adoptOrderService;
@Autowired
private IQuartzService quartzService;
@PostMapping("/notify")
public void notify(HttpServletRequest request){
System.out.println("支付宝异步回调!");
//获取支付宝POST过来反馈信息
try {
Map<String,String> params = new HashMap<String,String>();
Map<String,String[]> requestParams = request.getParameterMap();
for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
String name = (String) iter.next();
String[] values = (String[]) requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
valueStr = new String(valueStr);
params.put(name, valueStr);
}
String unionPaySn = params.get("out_trade_no");
PayBill payBill = payBillService.loadByUnionPaySn(unionPaySn);
if(payBill != null){
AlipayInfo info = alipayInfoService.getByShopId(payBill.getShop_id());
boolean signVerified = AlipaySignature.rsaCheckV1(params,
info.getAlipay_public_key(),
AlipayConfig.charset,
AlipayConfig.sign_type); //调用SDK验证签名
if(signVerified) {//验证成功
//商户订单号
String out_trade_no = unionPaySn;
//支付宝交易号
String trade_no = request.getParameter("trade_no");
//交易状态
String trade_status = request.getParameter("trade_status");
if(trade_status.equals("TRADE_FINISHED")){
//用户确认
}else if (trade_status.equals("TRADE_SUCCESS")){
//1. 改支付单状态
payBill.setState(1);
payBill.setUpdateTime(new Date());
payBillService.update(payBill);
String businessType = payBill.getBusinessType();
//2.再修改对应(领养)的订单状态 businessType businessKey 订单
if(PayConstants.BUSINESS_TYPE_ADOPT.equals(businessType)){//领养订单
Long orderId = payBill.getBusinessKey();
AdoptOrder order = adoptOrderService.getById(orderId);
order.setState(1);
adoptOrderService.update(order);
}
//移除针对当前订单的定时任务
quartzService.removeJob(out_trade_no);
}
}else {//验证失败
System.out.println("老宋,不要搞事");
//调试用,写文本函数记录程序运行情况是否正常
//String sWord = AlipaySignature.getSignCheckContentV1(params);
//AlipayConfig.logResult(sWord);
}
}
} catch (AlipayApiException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}