Java实现订单超时未支付自动取消的8种方法总结
Java实现订单超时未支付自动取消的8种方法总结
定时轮询
- 数据库定时轮询方式,实现思路比较简单。启动一个定时任务,每隔一定时间扫描订单表,查询到超时订单就取消。
- 优点:实现简单。
- 缺点:轮询时间间隔不好确定,占用服务器资源,影响数据库性能。
惰性取消
- 当查询订单信息时,先判断该订单是否超时,如果超时就先取消。
- 优点:实现简单。
- 缺点:影响查询之外的业务(如:统计、库存),影响查询效率。
JDK延迟队列
-
JDK延时队列DelayQueue是一个无界阻塞队列,该队列只有在延迟期满的时候才能从中获取元素。
-
简单实现代码demo如下,实际生产过程中会有专门的线程负责消息的入队与消费。
-
import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit;public class OrderDelayed implements Delayed {/*** 延迟时间*/private final Long time;/*** 订单编号*/public String orderNo;public OrderDelayed(String orderNo, long time, TimeUnit unit) {this.orderNo = orderNo;this.time = System.currentTimeMillis() + (time > 0 ? unit.toMillis(time) : 0);}@Overridepublic long getDelay(TimeUnit unit) {return time - System.currentTimeMillis();}@Overridepublic int compareTo(Delayed o) {OrderDelayed orderDelayed = (OrderDelayed) o;long diff = this.time - orderDelayed.time;if (diff <= 0) {return -1;} else {return 1;}} }
-
import java.util.concurrent.DelayQueue; import java.util.concurrent.TimeUnit;public class Test {public static void main(String[] args) {DelayQueue<OrderDelayed> delayQueue = new DelayQueue<>();delayQueue.put(new OrderDelayed("220101001", 8, TimeUnit.SECONDS));delayQueue.put(new OrderDelayed("220101002", 4, TimeUnit.SECONDS));System.out.println("订单延迟队列开始执行");while (true) {// 取队列头部元素是否过期OrderDelayed task = delayQueue.poll();if (task != null) {// 取消订单业务逻辑System.out.println("订单 ---> " + task.orderNo + " 已过期准备取消");}}} }
-
优点:效率高,任务触发时间延迟低。
-
缺点:异常恢复困难,集群扩展麻烦,内存占用。
时间轮
-
时间轮算法类似于时钟,会按某一个方向按固定频率轮动。可以用Netty的HashedWheelTimer来实现时间轮方法。
-
<dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.78.Final</version> </dependency>
-
import io.netty.util.HashedWheelTimer; import io.netty.util.Timeout; import io.netty.util.Timer; import io.netty.util.TimerTask;import java.util.concurrent.TimeUnit;public class Test {public static void main(String[] args) {// 初始化时间轮Timer timer = new HashedWheelTimer();// 定时任务TimerTask task1 = new TimerTask() {public void run(Timeout timeout) throws Exception {// 取消订单业务逻辑System.out.println("订单1已过期准备取消");}};// 注册此定时任务(延迟时间为5秒,也就是说5秒后订单过期)timer.newTimeout(task1, 5, TimeUnit.SECONDS);// 定时任务TimerTask task2 = new TimerTask() {public void run(Timeout timeout) throws Exception {// 取消订单业务逻辑System.out.println("订单2已过期准备取消");}};// 注册此定时任务(延迟时间为3秒,也就是说3秒后订单过期)timer.newTimeout(task2, 3, TimeUnit.SECONDS);} }
-
优点:效率高,任务触发时间延迟更低。
-
缺点:异常恢复困难,集群扩展麻烦,内存占用。
Redis过期回调
-
Redis的key过期回调事件,也能达到延迟队列的效果。
-
在redis.conf加入一条配置:
-
notify-keyspace-events Ex
-
监听配置
-
@Configuration public class RedisListenerConfig {@BeanRedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {RedisMessageListenerContainer container = new RedisMessageListenerContainer();container.setConnectionFactory(connectionFactory);return container;} }
-
Redis过期回调监听方法
-
@Component public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {super(listenerContainer);}@Overridepublic void onMessage(Message message, byte[] pattern) {// 过期key,可以设置成订单号String expiredKey = message.toString();// 取消订单业务逻辑System.out.println("订单 ---> " + expiredKey + " 已过期准备取消");} }
-
优点:数据不易丢失,集群扩展方便。
-
缺点:需要额外维护redis。
Redis有序集合
-
Redis的数据结构Zset,同样可以实现延迟队列的效果,主要利用它的score属性,redis通过score来为集合中的成员进行从小到大的排序。通过zadd命令向队列delayqueue 中添加元素,并设置score值表示元素过期的时间。
-
消费端轮询队列delayqueue, 将元素排序后取最小时间与当前时间比对,如小于当前时间代表已经过期移除key。
-
public void pollOrderQueue() {while (true) {Set<Tuple> set = jedis.zrangeWithScores(delayqueue, 0, 0);String value = ((Tuple) set.toArray()[0]).getElement();int score = (int) ((Tuple) set.toArray()[0]).getScore();int nowSecond = System.currentTimeMillis() / 1000);if (nowSecond >= score) {jedis.zrem(delayqueue, value);System.out.println(sdf.format(new Date()) + " removed key:" + value);}if (jedis.zcard(delayqueue) <= 0) {System.out.println(sdf.format(new Date()) + " zset empty ");return;}Thread.sleep(1000);} }
-
优点:数据不易丢失,集群扩展方便。\
-
缺点:可能重复消费同一key。
任务调度
-
使用任务调度中间件xxl-job、ScheduleX、Elastic-Job等来实现,设置一个调度时间cron,到达订单过期的调度时间时,触发任务执行取消订单业务逻辑。
-
例如使用xxl-job实现,订单创建时提交一个过期任务到xxl-job服务器,下面时执行器方法:
-
import com.xxl.job.core.handler.annotation.XxlJob; import org.springframework.stereotype.Component;@Component public class JobHandler {@XxlJob("payExpireJobHandler")public void payExpireJobHandler(String executorParam) {// 取消订单业务逻辑System.out.println("订单 ---> " + executorParam + " 已过期准备取消");} }
-
优点:时效性强,支持分布式。
-
缺点:实现复杂,维护成本高。
消息队列
-
使用RocketMQ、RabbitMQ、Kafka的延时消息,消息在发送到消息队列服务端后并不会立马投递,而是根据消息中的属性延迟固定时间后才投递给消费者。
-
RocketMQ发送延时消息的示例代码如下:
-
import com.aliyun.openservices.ons.api.Message; import com.aliyun.openservices.ons.api.ONSFactory; import com.aliyun.openservices.ons.api.Producer; import com.aliyun.openservices.ons.api.PropertyKeyConst; import com.aliyun.openservices.ons.api.SendResult; import java.util.Properties;public class Test {public static void main(String[] args) {Properties properties = new Properties();// AccessKey ID阿里云身份验证,在阿里云RAM控制台创建。properties.put(PropertyKeyConst.AccessKey, "XXX");// AccessKey Secret阿里云身份验证,在阿里云RAM控制台创建。properties.put(PropertyKeyConst.SecretKey, "XXX");// 设置TCP接入域名,进入消息队列RocketMQ版控制台实例详情页面的接入点区域查看。properties.put(PropertyKeyConst.NAMESRV_ADDR,"XXX");Producer producer = ONSFactory.createProducer(properties);// 在发送消息前,必须调用start方法来启动Producer,只需调用一次即可。producer.start();Message msg = new Message( // 您在消息队列RocketMQ版控制台创建的Topic。"Topic",// Message Tag,可理解为Gmail中的标签,对消息进行再归类,方便Consumer指定过滤条件在消息队列RocketMQ版服务器过滤。"tag",// Message Body可以是任何二进制形式的数据,消息队列RocketMQ版不做任何干预,需要Producer与Consumer协商好一致的序列化和反序列化方式。"Hello MQ".getBytes());// 设置代表消息的业务关键属性,请尽可能全局唯一。// 以方便您在无法正常收到消息情况下,可通过控制台查询消息并补发。// 注意:不设置也不会影响消息正常收发。msg.setKey("ORDERID_100");try {// 延时消息,在指定延迟时间(当前时间之后)进行投递。最大可设置延迟40天投递,单位毫秒(ms)。// 以下示例表示消息在3秒后投递。long delayTime = System.currentTimeMillis() + 3000;// 设置消息需要被投递的时间。msg.setStartDeliverTime(delayTime);SendResult sendResult = producer.send(msg);// 同步发送消息,只要不抛异常就是成功。if (sendResult != null) {System.out.println(new Date() + " Send mq message success. Topic is:" + msg.getTopic() + " msgId is: " + sendResult.getMessageId());}} catch (Exception e) {// 消息发送失败,需要进行重试处理,可重新发送这条消息或持久化这条数据进行补偿处理。System.out.println(new Date() + " Send mq message failed. Topic is:" + msg.getTopic());e.printStackTrace();}// 在应用退出前,销毁Producer对象。// 注意:如果不销毁也没有问题。producer.shutdown();} }
-
RocketMQ延时消息的订阅与普通消息订阅一致。
-
优点:高效,好扩展,支持分布式。
-
缺点:实现复杂,维护成本高。
相关文章:
Java实现订单超时未支付自动取消的8种方法总结
Java实现订单超时未支付自动取消的8种方法总结 定时轮询 数据库定时轮询方式,实现思路比较简单。启动一个定时任务,每隔一定时间扫描订单表,查询到超时订单就取消。优点:实现简单。缺点:轮询时间间隔不好确定&#x…...
android动态权限申请并展示权限使用说明
# ExplainPermissions 动态权限申请并展示权限使用说明 随着工信部对APP的一系列整治,现在用户对于APP在使用时动态申请的权限是比较敏感的,为了更好的用户体验,我们可以在权限动态申请的时候一并向用户展示所需要申请权限的使用说明。这样用…...
论文阅读《DPS-Net: Deep Polarimetric Stereo Depth Estimation》
论文地址:https://openaccess.thecvf.com/content/ICCV2023/html/Tian_DPS-Net_Deep_Polarimetric_Stereo_Depth_Estimation_ICCV_2023_paper.html 概述 立体匹配模型难以处理无纹理场景的匹配,现有的方法通常假设物体表面是光滑的,或者光照是…...
docker文档转译1
写在最前面 本文主要是转译docker官方文档。主题是Docker overview,这里是链接 Docker概述 Docker是一个用于开发、发布和运行应用程序的开放平台。Docker使你能够将应用程序与基础设施分离,从而可以快速交付软件。你可以使用相同的方法像管理应用程序…...
UE4 图片环形轮播 蓝图
【需求】 图片环形轮播 任意图片之间相互切换 切换图片所需时间均为1s 两个图片之间切换使用就近原则 播放丝滑无闪跳 【Actor的组成】 每个图片的轴心都在原点 【蓝图节点】...
饥荒Mod 开发(十):制作一把AOE武器
饥荒Mod 开发(九):物品栏排列 饥荒Mod 开发(十一):修改物品堆叠 前面的文章介绍了很多基础知识以及如何制作一个物品,这次制作一把武器,装备之后可以用来攻击怪物。 制作武器贴图和动画 1.1 制作贴图。 先准备一张武器的贴图&a…...
微服务实战系列之ZooKeeper(下)
前言 通过前序两篇关于ZooKeeper的介绍和总结,我们可以大致理解了它是什么,它有哪些重要组成部分。 今天,博主特别介绍一下ZooKeeper的一个核心应用场景:分布式锁。 应用ZooKeeper Q:什么是分布式锁 首先了解一下&…...
FFmpeg项目的组成
主要由三个部分组成: 工具 ffmpeg:用于音视频转码、转换ffplay:音视频播放器ffserver:流媒体服务器ffprobe:多媒体码流分析器 SDK 这个部分是供开发者使用的SDK,SDK是编译好的库。基本上每个平台都有对…...
计算机网络:数据链路层(广域网、PPP协议、HDLC协议)
今天又学会了一个知识,加油! 目录 一、广域网 二、PPP协议 1、PPP协议应满足的要求 2、PPP协议无需满足的要求 3、PPP协议的三个组成部分 4、PPP协议的状态图 5、PPP协议的帧格式 三、HDLC协议 1、HDLC的站(主站、从站、复合站&…...
Spring Boot i18n中文文档
本文为官方文档直译版本。原文链接 Spring Boot 支持本地化消息,因此您的应用程序可以满足不同语言偏好的用户。默认情况下,Spring Boot 会在类路径的根目录下查找是否存在消息资源包。 自动配置适用于已配置资源包的默认属性文件(默认为 mes…...
持久化存储 StorageClass
kubernetes从v1.4版本开始引入了一个新的资源对象StorageClass,用于标记存储资源的特性和性能。到v1.6版本时,StorageClass和动态资源供应的机制得到了完善,实现了存储卷的按需创建,在共享存储的自动化管理进程中能够实现了重要的…...
uni-app点击预览图片
<image :src"info.shopLogoUrl" tap"_previewImage(info.shopLogoUrl)" mode"widthFix" >_previewImage(image) {var imgArr [];imgArr.push(image);//预览图片uni.previewImage({urls: imgArr,current: imgArr[0]});},大佬地址...
【C++】POCO学习总结(十八):XML
【C】郭老二博文之:C目录 1、XML文件格式简介 1)XML文件的开头一般都有个声明,声明是可选 <?xml version"1.0" encoding"UTF-8"?>2)根元素:XML文件最外层的元素 3ÿ…...
京东体育用品销售数据分析与可视化系统
京东体育用品销售数据分析与可视化系统 前言数据爬取模块1. 数据爬取2. 数据处理3. 数据存储 数据可视化模块1. 数据查看2. 店铺商品数量排行3. 整体好评率4. 不同品牌市场占比5. 品牌差评率排名6. 品牌价格排名7. 品牌评论数量分布 创新点 前言 在体育用品行业,了…...
【STM32】STM32学习笔记-EXTI外部中断(11)
00. 目录 文章目录 00. 目录01. 中断系统02. 中断执行流程03. STM32中断04. NVIC基本结构05. NVIC优先级分组06. EXTI简介07. EXTI基本结构08. AFIO复用IO口09. EXTI框图10. 计数器模块11. 旋转编码器简介12. 附录 01. 中断系统 中断:在主程序运行过程中࿰…...
CSS BFC详解
概念 BFC(Block Formatting Context)是CSS中的一个概念,用于描述一个独立的渲染区域,其中的元素按照一定规则进行布局和渲染。 BFC具有以下特性和作用 清除浮动:当一个元素的float属性设置为left或right时ÿ…...
【C语言】实战项目——通讯录
引言 学会创建一个通讯录,对过往知识进行加深和巩固。 文章很长,要耐心学完哦! ✨ 猪巴戒:个人主页✨ 所属专栏:《C语言进阶》 🎈跟着猪巴戒,一起学习C语言🎈 目录 引言 实战 建…...
05 Rust 结构体
结构体 Rust 中的结构体(Struct)与元组(Tuple)都可以将若干个类型不一定相同的数据捆绑在一起形成整体,但结构体的每个成员和其本身都有一个名字,这样访问它成员的时候就不用记住下标了。 元组常用于非定…...
C语言预处理详解及其指令
预处理详解 1.预定义符号2.#define定义常量基本使用方法举例子如果在define定义的表示符后面加上分号会发生什么?用一下来解释 3. #define定义宏举例例1例2 4. 带有副作用的宏参数例如: 5. 宏替换的规则6. 宏函数的对比宏和函数的一个对比 7. #和##7.1 #运算符7.2 #…...
【数据结构—队列的实现】
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一、队列 1.1队列的概念及结构 二、队列的实现 2.1头文件的实现—Queue.h 2.2源文件的实现—Queue.c 2.3源文件的测试—test.c 三、测试队列实际数据的展示 3.…...
ASP.NET MVC实战之权限拦截Authorize使用
1,具体的实现方法代码如下 public class CustomAuthorizeAttribute : FilterAttribute, IAuthorizationFilter{/// <summary>/// 如果需要验证权限的时候,就执行进来/// </summary>/// <param name"filterContext"></par…...
java8实战 lambda表达式和函数式接口(上)
前言: 本博客对java8实战第三章的总结,也是上一篇博客行为化参数的延续,介绍一下函数式接口 Lambda表达式 lambda的表达式的结构由:参数,箭头,主体构成。 lambda示例 函数式接口: 先看上一篇…...
深度学习中的13种概率分布
1 概率分布概述 共轭意味着它有共轭分布的关系。 在贝叶斯概率论中,如果后验分布 p(θx)与先验概率分布 p(θ)在同一概率分布族中,则先验和后验称为共轭分布,先验称为似然函数的共轭先验。 多…...
C#基础知识 - 操作数与运算符篇2
C#基础知识 - 操作数与运算符篇 4.2 运算符4.2.1 按操作数个数分类4.2.2 按运算类型分类4.2.3 对运算符 、-- 的使用4.2.4 关系运算符:>、 < 、> 、<、 ! 、4.2.5 逻辑运算符:&& || ! ^ & |4.2.6 位运算符:~ 、^、 &…...
第十五章总结
一.输入/输出流 1.输入流 InputStrema类是字节输入流的抽象类,它是所有字节输入流的父类。 该类中所有方法遇到错误都会引发IOException异常。 read()方法:从输入流中读取数据的下一个字节。返回0~255的int字节值。如果因为已经到达流末尾而没有可用的…...
音频I2S
前言 基于网上资料对相关概念做整理汇总,部分内容引用自文后文章。 学习目标:简单了解相关概念、相关协议。 1 概述 数字音频接口DAI,即Digital Audio Interfaces,顾名思义,DAI表示在板级或板间传输数字音频信…...
小程序中的合法域名的作用及条件有哪些?
小程序的合法域名是指小程序项目中使用的各种接口、资源文件等所在的域名。在小程序开发中,需要将这些域名添加到小程序后台的“开发设置”-“服务器域名”中进行配置,才能够正常使用。 合法域名的作用: 1.作为小程序请求的 API 服务器域名…...
SpringData JPA 整合Springboot
1.导入依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0…...
打工人副业变现秘籍,某多/某手变现底层引擎-Stable Diffusion 黑白老照片上色修复
在这个时代,我们习惯于拥有高清、色彩丰富的照片,然而,那些古老的黑白色老照片由于年代的久远,往往会出现模糊、破损等现象。 那么今天要给大家介绍的是,用 Stable Diffusion 来修复老照片。 前段时间 ControlNet 的除了上线了“IP-Adapter”模型以外还增加另一个…...
第十三章总结
一.泛型 1.定义泛型类 泛型机制语法: 类名<T> 其中,T是泛型的名称,代表某一种类型。 【例13.6】创建带泛型的图书类 代码: 结果: 2.泛型的常规用法 (1)定义泛型类时声明多个变量 class MyClass<T1,T2…...
回收类型网站如何做/厦门seo怎么做
2019独角兽企业重金招聘Python工程师标准>>> 写操作MongoDB比传统数据库快的根本原因是Mongo使用的内存映射技术 - 写入数据时候只要在内存里完成就可以返回给应用程序,这样并发量自然就很高。而保存到硬体的操作则在后台异步完成。 MongoDB在…...
网站建设嘉兴/在线网站seo诊断
最新发布的Firefox 57 “Quantum”加入到Chrome和Edge的行列,现在只支持基于WebExtensions API的扩展插件,也就是基于跨浏览器的扩展架构,使用纯HTML、CSS和JavaScript来开发。基于旧架构的Firefox插件不能在Quantum上使用。\\WebExtensions …...
wordpress加菜单/品牌推广与传播怎么写
NEW关注Tech逆向思维视频号最新视频→【为什么要定期给眼睛拍照?】6月8日消息,美国当地时间周二,谷歌母公司Alphabet旗下自动驾驶卡车部门Waymo Via和网约车巨头Uber旗下货运业务部门Uber Freight宣布,两家公司已经签署了长期战略…...
微信小程序开发技术介绍/seo网站排名优化案例
目录缓冲区溢出的保护机制缓冲区溢出原理CANNARY(栈保护)FORTIFYNX(DEP)PIE(ASLR)RELRO检测工具checksec缓冲区溢出的保护机制 缓冲区溢出原理 缓冲区是内存中存放数据的地方。在程序试图将数据放到机器内存中的某一个位置的时候,因为没有足够的空间就会发生缓冲区溢…...
最好看的免费网站源码/网站排名软件
2019独角兽企业重金招聘Python工程师标准>>> 本文前戏较多,务实的同学可以直接跳到文末的结论。 由「钢的琴」网友脑洞大开延伸出了吉的他二的胡琵的琶,以及后来许嵩的「苏格拉没有底」,是否可以再拓展一下,得到哥本不…...
做美食网站需求分析报告/网站建设网络营销
1、 boot分区 大小:一般300Mb左右 作用:引导分区,包含了系统启动的必要内核文件,即使根分区损坏也能正常引导启动,一般这些文件所占空间在200M以内。 分区建议:分区的时候可选100M-500M之间,如果空间足够…...