取消超时订单及延迟处理方案
- 使用场景
- 方案
- 优化
1.使用场景
12306订单30分钟自动取消?
淘宝订单超过2小时自动取消?
美团外卖订单超过30分钟自动取消?
抢购如何处理?被动更新 + crond 主动更新两种方式,因为是抢购,下单扣库存,5分钟不支付马上过期恢复库存。
订单支付的时候再去校验时间是否过期,查询校验一次、订单支付校验一次
另请注意,请判断好支付完成回调的验证,因为用户下单后,20几分钟后再点击付款,再到支付页面停留,时间已经超过30分钟,然后支付成功回调时请注意判断验证回调的信息
订单如果一直在支付页面,不支付呢?如果支付完成回跳,订单恰好过期了,直接将订单设置为待退款状态,然后走另一个定时任务完成退款。微信支付可以设置订单超时自动关闭,那么一直不支付的话,调起支付会提示订单已关闭。
2.方案
被动过期策略
方案1:
在每次查询这个订单时候检查过期,被动过期。
比如,查询订单细节时,再去检查是否过期然后再处理。
当然,如果这条数据不被访问可能永远不会过期,直到有人访问它。
有点像薛定谔的猫,在你打开盒子(检查订单)之后,才知道它是否过期。所以叫被动过期。
方案2:redis
在创建订单时候,将订单id作为key存入redis,过期时间为30分钟。
用户查询待支付订单列表时,检查id是否还存于redis,不存在则关闭订单。发起支付时同理关闭订单,订单支付成功回调时则将订单置为退款状态。
redis主动过期策略:redis实现延时队列的两种方式redis实现延时队列的两种方式_暮色里de白雪檐的博客-CSDN博客_redis实现延时队列
主动过期策略
方案3:数据库轮训、定时任务:springboot的自带的schedule的注解、用quartz,定时器
写个定时任务,每一分钟查询未支付订单,如果其时间超过大于等于创建订单时间30分钟,则关闭订单
优点:实现简单、无技术难点、异常恢复、支持分布式/集群环境
缺点:性能低
方案4:java延迟队列delayQueue
优点:实现简单、性能较好
缺点:异常恢复困难、分布式/集群实现困难
1.知识点
delayQueue相关api:DelayQueue是java.util.concurrent中提供的一个类。
DelayQueue小结
DelayQueue是一个有序的无界BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象在到期时才能从队列中取走。
DelayQueue只能添加实现了Delayed接口的对象,不能将null元素放置到这种队列中。
BlockingQueue中add,offer,put方法区别
- add:将指定的元素插入到此队列中,在成功时返回 true,如果当前没有可用空间,则抛出 IllegalStateException,该方式为非阻塞添加。
- offer:将指定元素插入到此队列的尾部(如果立即可行且不会超出此队列的容量),在成功时返回 true,如果此队列已满,则返回 false,此方法通常要优于 add 方法,该方式为非阻塞添加。
- put:将指定元素插入到此队列的尾部,该方式为阻塞添加,则等待空间变得可用。
- take:出队,将符合条件的元素取出,take()方法阻塞等待,有过期元素时继续。
线程池知识点
SpringBoot中CommandLineRunner和ApplicationRunner接口解析和使用
2.步骤
2.1 创建需要延迟处理的类实现接口Delayed,重写getDelay和compareTo方法,如延迟订单类
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;public class OrderDelay implements Delayed{private Long orderId;private Long createTime; // 创建时间public OrderDelay(Long orderId,Long createTime){this.orderId = orderId;this.createTime = createTime;}// 判断对象是否过期(小于等于0时过期)@Overridepublic long getDelay(TimeUnit unit) {return createTime+24*60*60*1000l-System.currentTimeMillis(); // 创建订单24小时未支付即过期}// 根据对象过期时间排序, 哪个元素最早到过期时间, 就排在前面 @Overridepublic int compareTo(Delayed delayed) {OrderDelay that = (OrderDelay)delayed;if(this.createTime>that.createTime){return 1;}else if(this.createTime==that.createTime){return 0;}else {return -1;}}public Long getOrderId() {return orderId;}public Long getCreateTime() {return createTime;}}
2.2 定义一个Job类,实现CommandLineRunner接口,用于在项目启动后执行该任务,通过take()方法从队列中获取到指定对象出列
@Component
public class OrderDelayQueueJob implements CommandLineRunner {// 创建一个延迟队列public static DelayQueue<OrderDelay> delayQueue = new DelayQueue();@AutowiredOrderMapper orderMapper;@AutowiredAliPayService aliPayService;@AutowiredVxPayService vxPayService;// 超过24小时未支付订单自动关闭private void orderTask() {List<Order> payingOrders = orderMapper.listPayingOrder();for(int i=0; i<payingOrders.size(); i++) {Order order = payingOrders.get(i);OrderDelay orderDelay = new OrderDelay(order.getId(), order.getCreateTime().toInstant().toEpochMilli());OrderDelayQueueJob.delayQueue.add(orderDelay);}while (true) {try {OrderDelay orderDelay = OrderDelayQueueJob.delayQueue.take();Integer orderStatus = orderMapper.getOrderStatus(orderDelay.getOrderId());if(orderStatus == 0) {orderMapper.cancelOrder(orderDelay.getOrderId());Order order = orderMapper.getOrderById(orderDelay.getOrderId());if(order.getPayType()==1) { // 支付宝订单try {aliPayService.closePay(order.getId());} catch (AlipayApiException e) {e.printStackTrace();}}else if(order.getPayType()==2) { // 微信订单try {vxPayService.closeOrder(order.getId());} catch (Exception e) {e.printStackTrace();}}else {;}LogUtil.info("待支付订单队列剩余:"+OrderDelayQueueJob.delayQueue.size()+",订单关闭:"+orderDelay.getOrderId()+",订单创建时间:"+LocalDateTime.ofInstant(Instant.ofEpochMilli(orderDelay.getCreateTime()), ZoneId.of("GMT+8")));}} catch (InterruptedException e) {break;}}}@Overridepublic void run(String... args) throws Exception {// 自动取消订单开启ThreadUtil.execute(this::orderTask);}}
2.3 在创建订单时,将该订单放入延迟队列中
Order order = new Order();
order.setOrderNumber(String.valueOf(now.toInstant().toEpochMilli()));
order.setUserId(userId);
order.setSubjectName(productName);
order.setProductType(productType);
order.setPayAmount(payAmount);
order.setYears(years);
order.setOrderStatus(0);
order.setPayType(1);
order.setCreateTime(now);
order.setIsDelete(0);
orderMapper.addOrder(order);
OrderDelay orderDelay = new OrderDelay(order.getId(), order.getCreateTime().toInstant().toEpochMilli());
OrderDelayQueueJob.delayQueue.add(orderDelay); // 将延迟订单放入延迟队列中
2.4当客户取消订单的时候,从Queue里面删除订单信息
方案5:消息队列:mq
使用RabbitMq 实现 RabbitMq实现延迟队列
优点: 开源,现成的稳定的实现方案;
缺点: RabbitMq是一个消息中间件;延迟队列只是其中一个小功能,如果团队技术栈中本来就是使用RabbitMq那还好,如果不是,那为了使用延迟队列而去部署一套RabbitMq成本有点大;
思考归纳:
1.延迟处理方案1.延迟队列能做什么?
在我们的业务中通常会有一些需求是这样的:1. 淘宝订单业务:下单之后如果三十分钟之内没有付款就自动取消订单。2. 饿了吗订餐通知:下单成功后60s之后给用户发送短信通知。那么这类业务我们可以总结出一个特点:需要延迟工作。
由此的情况,就是我们的DelayQueue应用需求的产生。2.如何实现延迟队列3.springboot如何整合延迟队列4.性能如何优化?如何做到高可靠、高可用?对于订单超时场景处理的思考(单机):https://zhuanlan.zhihu.com/p/99406453
取消超时订单及延迟处理方案相关推荐
- java订单到期自动取消_订单自动过期实现方案
需求分析: 24小时内未支付的订单过期失效. 解决方案 被动设置:在查询订单的时候检查是否过期并设置过期状态. 定时调度:定时器定时查询并过期需要过期的订单. 延时队列:将未支付的订单放入一个延时队列 ...
- 拼夕夕订单超时未支付自动关闭实现方案!
" 在开发中,往往会遇到一些关于延时任务的需求.例如:生成订单 30 分钟未支付,则自动取消:生成订单 60 秒后,给用户发短信. 对上述的任务,我们给一个专业的名字来形容,那就是延时任务. ...
- java订单超时取消设计_PHP如何实现处理过期或者超时订单的,并还原库存
文章来自:php自学中心网站 链接:http://www.startphp.cn/front/php/122378.html 作者:磊丰 商务合作:请加微信(QQ):2230304070 视频教程 码 ...
- java订单超时取消设计_quartz框架和关闭超时订单
Quartz Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用.Quartz可以用来创建简单或为运 ...
- 下单后半小时未付款订单自动取消的实现,延迟队列
下单后半小时未付款订单自动取消的实现,延迟队列 类似的需要: 订单的评论如果7天未评价,系统需要自动产生一条评论 订单的15天之后未点击收货,系统需要自动更改为已收货. ... 因为是需要一个常驻进程 ...
- 分布式锁和mysql事物扣库存_这个是真的厉害,高并发场景下的订单和库存处理方案,讲的很详细了!...
前言 之前一直有小伙伴私信我问我高并发场景下的订单和库存处理方案,我最近也是因为加班的原因比较忙,就一直没来得及回复.今天好不容易闲了下来想了想不如写篇文章把这些都列出来的,让大家都能学习到,说一千道 ...
- php 订单过期处理,PHP实现处理过期或者超时订单,并还原库存
订单是我们在日常开发中经常会遇到的一个功能,最近在做一个订单过期与超时的开发.订单过期与超时就不用我解释了吧,其实两者都是同一个问题来着,就是订单未支付的处理,我们要做的是对这些未支付的订单到了一定时 ...
- 使用延时队列搞定超时订单处理
1.延时队列使用场景: 那么什么时候需要用延时队列呢?常见的延时任务场景 举栗子: 订单在30分钟之内未支付则自动取消. 重试机制实现,把调用失败的接口放入一个固定延时的队列,到期后再重试. 新创建的 ...
- java分布式库存系统_这个是真的厉害,高并发场景下的订单和库存处理方案,讲的很详细了!...
前言 之前一直有小伙伴私信我问我高并发场景下的订单和库存处理方案,我最近也是因为加班的原因比较忙,就一直没来得及回复.今天好不容易闲了下来想了想不如写篇文章把这些都列出来的,让大家都能学习到,说一千道 ...
最新文章
- Spring学习(23)--- AOP之Introductions应用
- 开源跳板机(堡垒机)Jumpserver v0.2.0 使用说明
- ECSHOP去版权标志删除Powered by ECShop(转)
- linux下qt实现计算器,QT实现计算器
- 第19章,运维自动化之系统安装
- 怎么定义int_面试官问:MySQL的自增ID用完了,怎么办?!
- 浅谈:Hadoop、spark、SaaS、PaaS、IaaS、云计算
- 从入门到入土:Python爬虫学习|实例练手|爬取百度翻译|Selenium出击|绕过反爬机制|
- 谷歌浏览器禁止右滑返回历史_早报:拼多多加大百亿补贴力度;嫦娥五号择机返回地球;贾跃亭再成被执行人;微信订阅号页面改版...
- 爱立信实习总结之面试心得
- 微信公众平台开发 整合百度云开放平台与微信开放平台
- 2021年危险化学品经营单位主要负责人新版试题及危险化学品经营单位主要负责人考试试题
- 免费下载《WindowsPE权威指南》百度云
- 软件构造笔记——Rep Invariantand Abstraction Function
- 基于蓝牙的车载OBD系统
- 如何搭建一个站内搜索引擎(一) 第1章 写在最前
- cdr怎么算曲线周长_CorelDRAW X3计算封闭曲线长度和面积
- 查看世界编程语言排行榜
- java多线程过桥问题_(java)农夫过桥问题
- 关于 AlphaBlend 和 32bpp 的反锯齿图标