java订单到期自动取消_订单自动过期实现方案
需求分析:
24小时内未支付的订单过期失效。
解决方案
被动设置:在查询订单的时候检查是否过期并设置过期状态。
定时调度:定时器定时查询并过期需要过期的订单。
延时队列:将未支付的订单放入一个延时队列中,依次取出过期订单。
过期提醒:reids支持将一个过期的key(订单号)通知给客户端,根据过期的订单号进行相应的处理。
1. 被动设置
这个太简单了,就是在查询的时候判断是否失效,如果失效了就给他设置失效状态。但是弊端也很明显,每次查询都要对未失效的订单做判断,如果用户不查询,订单就不失效,那么如果有类似统计失效状态个数的功能,将会受到影响,所以只能适用于简单独立的场景。简直low爆了。
2. 定时调度
这种是常见的方法,利用一个定时器,在设置的周期内轮询检查并处理需要过期的订单。
具体实现有基于Timer的,有基于Quartz,还有springboot自带的Scheduler,实现起来比较简单。
就写一下第三个的实现方法吧:
启动类加上注解@EnableScheduling
新建一个定时调度类,方法上加上@Scheduled注解,如下图那么简单。
弊端
不能够精准的去处理过期订单,轮询周期设置的越小,精准度越高,但是项目的压力越大,我们上一个项目就有这种状况,太多定时器在跑,项目运行起来比较笨重。
而且需要处理的是过期的订单,但是要查询所有未支付的订单,范围大。对于大订单量的操作不合适。
3. 延时队列
基于JDK的实现方法,将未支付的订单放到一个有序的队列中,程序会自动依次取出过期的订单。
如果当前没有过期的订单,就会阻塞,直至有过期的订单。由于每次只处理过期的订单,并且处理的时间也很精准,不存在定时调度方案的那两个弊端。
实现:
1.首先创建一个订单类OrderDelayDto需要实现Delayed接口。然后重写getDelay()方法和compareTo()方法,只加了订单编号和过期时间两个属性。
这两个方法很重要,
getDelay()方法实现过期的策略,比如,订单的过期时间等于当前时间就是过期,返回负数就代表需要处理。否则不处理。
compareTo()方法实现订单在队列中的排序规则,这样即使后面加入的订单,也能加入到排序中,我这里写的规则是按照过期时间排序,最先过期的排到最前面,这一点很重要,因为排在最前面的如果没有被处理,就会进入阻塞状态,后面的不会被处理。
import lombok.Data;
import java.util.Date;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/**
* @author mashu
* Date 2020/5/17 16:25
*/
@Data
public class OrderDelayDto implements Delayed {
/**
* 订单编号
*/
private String orderCode;
/**
* 过期时间
*/
private Date expirationTime;
/**
* 判断过期的策略:过期时间大于等于当前时间就算过期
*
* @param unit
* @return
*/
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(this.expirationTime.getTime() - System.currentTimeMillis(), TimeUnit.NANOSECONDS);
}
/**
* 订单加入队列的排序规则
*
* @param o
* @return
*/
@Override
public int compareTo(Delayed o) {
OrderDelayDto orderDelayDto = (OrderDelayDto) o;
long time = orderDelayDto.getExpirationTime().getTime();
long time1 = this.getExpirationTime().getTime();
return time == time1 ? 0 : time < time1 ? 1 : -1;
}
}
其实这样已经算是写好了。我没有耍你。
写个main 方法测试一下,创建两个订单o1和o2,放入到延时队列中,然后while()方法不断的去取。
在此方法内通过队列的take()方法获得已过期的订单,然后做出相应的处理。
public static void main(String[] args) {
DelayQueue queue = new DelayQueue<>();
OrderDelayDto o1 = new OrderDelayDto();
//第一个订单,过期时间设置为一分钟后
o1.setOrderCode("1001");
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE, 1);
o1.setExpirationTime(calendar.getTime());
OrderDelayDto o2 = new OrderDelayDto();
//第二个订单,过期时间设置为现在
o2.setOrderCode("1002");
o2.setExpirationTime(new Date());
//往队列中放入数据
queue.offer(o1);
queue.offer(o2);
// 延时队列
while (true) {
try {
OrderDelayDto take = queue.take();
System.out.println("订单编号:" + take.getOrderCode() + " 过期时间:" + take.getExpirationTime());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
我故意把第二个订单的过期时间设置为第一个订单之前,从结果可以看出,他们已经自动排序把最先过期的排到了最前面。
第一个订单的失效时间是当前时间的后一分钟,结果也显示一分钟后处理了第一条订单。
2.然而通常情况下,我们会使用多线程去取延时队列中的数据,这样即使线程启动之后也能动态的向队列中添加订单。
创建一个线程类OrderCheckScheduler实现Runnable接口,
添加一个延时队列属性,重写run()方法,在此方法内通过队列的take()方法获得已过期的订单,然后做出相应的处理。
import java.util.concurrent.DelayQueue;
/**
* @author mashu
* Date 2020/5/17 14:27
*/
public class OrderCheckScheduler implements Runnable {
// 延时队列
private DelayQueue queue;
public OrderCheckScheduler(DelayQueue queue) {
this.queue = queue;
}
@Override
public void run() {
while (true) {
try {
OrderDelayDto take = queue.take();
System.out.println("订单编号:" + take.getOrderCode() + " 过期时间:" + take.getExpirationTime());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
好了,写个方法测试一下:
public static void main(String[] args) {
// 创建延时队列
DelayQueue queue = new DelayQueue<>();
OrderDelayDto o1 = new OrderDelayDto();
//第一个订单,过期时间设置为一分钟后
o1.setOrderCode("1001");
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE, 1);
o1.setExpirationTime(calendar.getTime());
OrderDelayDto o2 = new OrderDelayDto();
//第二个订单,过期时间设置为现在
o2.setOrderCode("1002");
o2.setExpirationTime(new Date());
//运行线程
ExecutorService exec = Executors.newFixedThreadPool(1);
exec.execute(new OrderCheckScheduler(queue));
//往队列中放入数据
queue.offer(o1);
queue.offer(o2);
exec.shutdown();
}
结果和上面的一样,图就不截了,相信我。
过期提醒
基于redis的过期提醒功能,听名字就知道这个方案最是纯真、最直接的,就是单纯处理过期的订单。
修改个redis的配置吧先,因为redis默认不开启过期提醒。
notify-keyspace-events改为notify-keyspace-events "Ex"
写一个类用来接收来自redis的暖心提醒OrderExpirationListener,继承一下KeyExpirationEventMessageListener抽象类。重写onMessage()方法,在此方法中处理接收到的过期key.
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* @author mashu
* Date 2020/5/17 23:01
*/
@Component
public class OrderExpirationListener extends KeyExpirationEventMessageListener {
public OrderExpirationListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
@Override
public void onMessage(Message message, byte[] pattern) {
final String expiredKey = message.toString();
System.out.println("我过期了" + expiredKey+"当前时间:"+new Date());
}
}
ok,向redis中存入一个订单,过期时间为1分钟。
redis.set("orderCode/10010", "1", 1L, TimeUnit.MINUTES);
System.out.println("redis存入订单号 key: orderCode/10010,value:1,过期时间一分钟,当前时间"+new Date());
运行结果:
除此之外还有用到消息队列的。夜深了,我得玩会游戏了。
没有绝对的好方案,只有在不同场景下的更合适的方案。随着需求的变化,技术的革新,方案也会不断的被优化和迭代,唯一不变的是工资。
关于找一找教程网
本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。
本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。
[订单自动过期实现方案]http://www.zyiz.net/tech/detail-145615.html
java订单到期自动取消_订单自动过期实现方案相关推荐
- 【项目实战】Redis使用场景之待支付订单自动取消、订单自动收货
一.使用背景 很多业务场景,例如订单过期自动删除,订单几天后自动好评,这些常用操作可以通过定时任务,数据库轮询做,但是订单量大的情况可能会对数据库产生大的压力. 二.Redis的key过期推送功能原理 ...
- mysql订单详情的设计_订单功能模块设计与实现
在商城项目中,之前我们介绍了购物车功能模块的实现,商品加入到购物车之后,就是到购物车结算,然后显示购物车的商品列表,点击去结算,然后到了未提交前的订单列表, 点击提交订单后,生成此订单,返回订单的订单 ...
- 电脑微信关闭自动保存_微信自动保存图片功能怎么取消掉 微信自动保存图片关闭设置方法...
微信是大家在手机上使用最频繁的一个app应用,而大多时候,手机内存不够用,第一件事就是清理微信占用内存了.其实微信用户只需要在使用微信时将微信自动保存图片关闭就能够省下大量内存了,那么微信自动保存图片 ...
- 程序设计java银行自动取款机_模拟自动取款机系统(JAVA)
import java.io.*; /*该类为实现客户信息及部分功能*/ class Account { private String code =null; private String name ...
- 简易自动电阻测试仪_开始自动测试您的网站的简单方法
简易自动电阻测试仪 by Adam Kelly 通过亚当凯利 开始自动测试您的网站的简单方法 (The easy way to start automatically testing your web ...
- word 流水号 自动增加_以自动组卷软件为例浅谈Python自动化办公
Python作为一种学习门槛低.简洁并且编码效率高的热门编程语言,在很多行业都有广泛的应用.在自动化办公领域,掌握一点Python知识就能够把或复杂或繁琐的工作交由计算机自动处理,堪称解放劳动力的利器 ...
- win7如何设置通电自动开机_电脑自动开机,教您怎么设置电脑自动开机
上班族每天上班打开电脑的时间都差不多在那个时间,都开电脑太麻烦,困则思变,可以设定电脑自动开机.这样一来,在还没来上班前电脑就已经开机,等来上班后即可直接运行电脑,不用再开机,下面,小编就来跟大家讲解 ...
- mysql数据自动备份_每天自动备份MySQL数据库的shell脚本
经常备份数据库是一个好习惯,虽然数据库损坏或数据丢失的概率很低,但一旦发生这种事情,后悔是没用的.一般网站或应用的后台都有备份数据库的功能按钮,但需要去手工执行.我们需要一种安全的,每天自动备份的方法 ...
- 笔记本电脑频繁自动重启_笔记本电脑自动重启是什么原因
使用电脑很长一段时间就会出现各种各样的问题,但不管出了什么问题,只要电脑能打开有一种方法可以解决的问题,但有时电脑会莫名其妙的重启,电脑爱好者我们有点不知所措.尤其是办公室人员做了很长时间的工作,想要 ...
最新文章
- 四宫格效果 css_【深度教研】智力游戏“九宫格” 集体教研活动纪实
- andriod数据库的开发
- 计算机网络经典试题答案,2016年计算机软考网络工程师经典练习试题及答案
- inline详细讲解【C语言】
- POJ-3764 01-Trie
- 知识共享平台开发-BUG[2014-11-27]
- 【selenium】窗口操作
- 推荐几个适合上班摸鱼的神操作!
- 模数转换器(ADC)
- 有了这几款软件,就不用纠结拍照识别植物的软件哪个好了
- 在Mac上修复问题硬盘是如何操作的
- 企业SSH 密钥管理
- Windows10彻底卸载VMWare虚拟机
- Python爬取马蜂窝城市游记
- 机械臂末端执行器汇总
- Mac版Ps、AE、PR不能突然使用?
- 小程序开发学习(3)---.wxss详解篇
- Java判断一个序列是否可由给定序列通过栈操作获得(ABCDEF)
- 骑行318、 2016.7.16
- r spgm 语言_R语言如何处理incorrect number of dimensions错误?