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.…...

【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...

C++_哈希表
本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说,直接开始吧! 一、基础概念 1. 哈希核心思想: 哈希函数的作用:通过此函数建立一个Key与存储位置之间的映射关系。理想目标:实现…...

6.9-QT模拟计算器
源码: 头文件: widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMouseEvent>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);…...

spring boot使用HttpServletResponse实现sse后端流式输出消息
1.以前只是看过SSE的相关文章,没有具体实践,这次接入AI大模型使用到了流式输出,涉及到给前端流式返回,所以记录一下。 2.resp要设置为text/event-stream resp.setContentType("text/event-stream"); resp.setCharacter…...

Axure Rp 11 安装、汉化、授权
Axure Rp 11 安装、汉化、授权 1、前言2、汉化2.1、汉化文件下载2.2、windows汉化流程2.3、 macOs汉化流程 3、授权 1、前言 Axure Rp 11官方下载链接:https://www.axure.com/downloadthanks 2、汉化 2.1、汉化文件下载 链接: https://pan.baidu.com/s/18Clf…...

虚拟机网络不通的问题(这里以win10的问题为主,模式NAT)
当我们网关配置好了,DNS也配置好了,最后在虚拟机里还是无法访问百度的网址。 第一种情况: 我们先考虑一下,网关的IP是否和虚拟机编辑器里的IP一样不,如果不一样需要更改一下,因为我们访问百度需要从物理机…...

简单聊下阿里云DNS劫持事件
阿里云域名被DNS劫持事件 事件总结 根据ICANN规则,域名注册商(Verisign)认定aliyuncs.com域名下的部分网站被用于非法活动(如传播恶意软件);顶级域名DNS服务器将aliyuncs.com域名的DNS记录统一解析到shado…...

Unity-ECS详解
今天我们来了解Unity最先进的技术——ECS架构(EntityComponentSystem)。 Unity官方下有源码,我们下载源码后来学习。 ECS 与OOP(Object-Oriented Programming)对应,ECS是一种完全不同的编程范式与数据架构…...