需求分析: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());

运行结果:

除此之外还有用到消息队列的。夜深了,我得玩会游戏了。

没有绝对的好方案,只有在不同场景下的更合适的方案。随着需求的变化,技术的革新,方案也会不断的被优化和迭代,唯一不变的是工资。

java控制订单过期时间_订单自动过期实现方案相关推荐

  1. redis查看key的过期时间_面试官:你在Redis中设置过带过期时间的Key吗?

    点击上方小伟后端笔记关注公众号 每天阅读Java干货文章 熟悉Redis的同学应该知道,Redis的每个Key都可以设置一个过期时间,当达到过期时间的时候,这个key就会被自动删除. 在为key设置过 ...

  2. redis list设置过期时间_面试官:你在Redis中设置过带过期时间的Key吗?

    点击上方小伟后端笔记关注公众号 每天阅读Java干货文章 熟悉Redis的同学应该知道,Redis的每个Key都可以设置一个过期时间,当达到过期时间的时候,这个key就会被自动删除. 在为key设置过 ...

  3. 设置过期时间_在Redis中设置了过期时间的Key,需要注意哪些问题?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:千山qianshan juejin.im/post/5d6b ...

  4. redistemplate hash 过期时间_完美!谈谈Java中时间控制的几种解决方案

    前言 需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可 ...

  5. mysql 验证码过期时间_简单的行为控制管理方法,自动登录,session定时验证码过期...

    public functionindex() {if(IS_POST) {//尝试登陆次数增加 behavior_function(__FUNCTION__, __CLASS__,1);}else{$ ...

  6. java订单实现的_订单的处理原理及代码实现.

    上一篇博文我们已经介绍了购物车的原理及实现, 那么购物车再往后就是提交订单了, 订单的实现方式是怎么样的呢? 那么下面就让我们来一起学习下. 提交订单有几个关键点: 1, 用户必须登录 2, 购物车必 ...

  7. python订单管理系统功能_订单管理系统的基本功能有哪些?

    订单管理系统是通过统一的订单管理和分配,给用户提供整合的一站式供应链服务,让仓储,运输和订单形成一个有机的整体,从而满足物流信息化的需求,今天德米萨就给大家具体介绍下订单管理系统的基本功能有哪些. 1 ...

  8. springboot 集成jwt设置过期时间_传说中的jwt,我们来征服一下

    原创:猿逻辑,欢迎分享,转载请保留出处. 本文的完整示例代码,见github仓库.小q只在文中介绍最关键的代码块. https://github.com/yuanluoji/purestart-spr ...

  9. mysql bin的过期时间_Mysql设置binlog过期时间并自动删除

    问题: Mysql数据库由于业务原因,数据量增长迅速,binlog日志会增加较多,占用大部分磁盘空间. 解决方案: 出于节约空间考虑,可进行删除多余binary日志,并设置定期删除操作. 1.查看bi ...

最新文章

  1. Codeforces Round #698 (Div. 2) D. Nezzar and Board(一步步推出来,超级清晰,不猜结论,看不懂来打我 ~ 好题 )
  2. 嫦娥五号样本研究登上Science:比阿波罗号样本“年轻”10亿年,填补了月球年龄研究空白期...
  3. windows下安装redis以及一些常规操作
  4. 实验二:网络嗅探与欺骗
  5. MongoDB基本概念学习 - 文档
  6. php的exportexcel,PHPExcel export网络或本地图片到excel
  7. VTK:命名颜色用法实战
  8. 深度学习(二十四)——L2 Normalization, Attention(1)
  9. word vba 读出光标所在的列数_word高效技巧:这几个表格操作让工作更快速
  10. JSP 2.2规范 对jsp:useBean的解释
  11. app_start(‘com.ss.android.ugc.aweme‘) 打不开app 无反应 Activity not started, unable to resolve Intent {
  12. php 错误503的原因,CentOS + Apache2.4 + PHP5.6 FPM报503错误
  13. 使用Apple设备的看过来,你的Apple账户为什么莫名其妙地被扣款!
  14. 在win10系统安装linux系统,win10系统安装Linux子系统的操作方法
  15. 专知原创和整理了一些中英文机器学习从入门到精通的资料,和大家分享一下
  16. 拒绝验证因为有你,我不孤单
  17. 前端实现小型打包工具
  18. Android大赛首轮获奖作品解析
  19. 电磁场与仿真软件(13)
  20. Matlab2010与VS2010初吻(一)

热门文章

  1. 卡迈克尔数 Carmichael Numbers(挑战程序设计竞赛)
  2. android保存网站在桌面,如何将网站保存到手机桌面(Android)?
  3. Jira实战 | 版本和组件管理
  4. PADS画2.54mm排针
  5. vue3的中间值思维
  6. 如何使用IxNetwork软件进行Avnu gPTP一致性测试?
  7. 安全断路器市场现状及未来发展趋势分析
  8. 【论文Word排版】使用多级列表设置论文序号
  9. 【GNN报告】ICT敖翔:图机器学习应对金融欺诈对抗攻击
  10. git 命令 简单介绍