目录
- 概述
- 示例
- 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 {@Beanpublic 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();}@Beanpublic 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 {@Overrideprotected 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 {@Autowiredprivate SchedulerFactoryBean schedulerFactory;/** @param scheduler* @param jobName* @param cls* @param params* @param time 时间表达式 定时任务执行的时间 已知 执行的时间 Date--->0/1 * * * * ?*/@Overridepublic 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();}}@Overridepublic 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 {@Autowiredprivate IPayBillService payBillService;@Autowiredprivate IAdoptOrderService adoptOrderService;@Overridepublic 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 {@Autowiredprivate PetMapper petMapper;@Autowiredprivate UserMapper userMapper;@Autowiredprivate AdoptOrderMapper adoptOrderMapper;@Autowiredprivate UserAddressMapper userAddressMapper;@Autowiredprivate OrderAddressMapper orderAddressMapper;@Autowiredprivate EmployeeMapper employeeMapper;@Autowiredprivate PayBillMapper payBillMapper;@Autowiredprivate IPayBillService payBillService;@Autowiredprivate IQuartzService quartzService;/*** 领养订单结算*/@Override@Transactionalpublic 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的idInteger 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;}@Overridepublic PageList<AdoptOrder> queryAdmin(AdoptOrderQuery query, Long loginInfoId) {//1.通过loginInfoID查询出EmployeeEmployee employee = employeeMapper.loadByLoginInfoId(loginInfoId);//2.如果employee中的shopID不为null,就是店铺。否则就是平台员工if (employee.getShop_id() != null) {query.setShopId(employee.getShop_id());}return super.queryPage(query);}@Overridepublic 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 {@Autowiredprivate IPayBillService payBillService;@Autowiredprivate IAlipayInfoService alipayInfoService;@Autowiredprivate IAdoptOrderService adoptOrderService;@Autowiredprivate 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();}}
}