当前位置: 首页 > news >正文

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种方法总结 定时轮询 数据库定时轮询方式&#xff0c;实现思路比较简单。启动一个定时任务&#xff0c;每隔一定时间扫描订单表&#xff0c;查询到超时订单就取消。优点&#xff1a;实现简单。缺点&#xff1a;轮询时间间隔不好确定&#x…...

android动态权限申请并展示权限使用说明

# ExplainPermissions 动态权限申请并展示权限使用说明 随着工信部对APP的一系列整治&#xff0c;现在用户对于APP在使用时动态申请的权限是比较敏感的&#xff0c;为了更好的用户体验&#xff0c;我们可以在权限动态申请的时候一并向用户展示所需要申请权限的使用说明。这样用…...

论文阅读《DPS-Net: Deep Polarimetric Stereo Depth Estimation》

论文地址&#xff1a;https://openaccess.thecvf.com/content/ICCV2023/html/Tian_DPS-Net_Deep_Polarimetric_Stereo_Depth_Estimation_ICCV_2023_paper.html 概述 立体匹配模型难以处理无纹理场景的匹配&#xff0c;现有的方法通常假设物体表面是光滑的&#xff0c;或者光照是…...

docker文档转译1

写在最前面 本文主要是转译docker官方文档。主题是Docker overview&#xff0c;这里是链接 Docker概述 Docker是一个用于开发、发布和运行应用程序的开放平台。Docker使你能够将应用程序与基础设施分离&#xff0c;从而可以快速交付软件。你可以使用相同的方法像管理应用程序…...

UE4 图片环形轮播 蓝图

【需求】 图片环形轮播 任意图片之间相互切换 切换图片所需时间均为1s 两个图片之间切换使用就近原则 播放丝滑无闪跳 【Actor的组成】 每个图片的轴心都在原点 【蓝图节点】...

饥荒Mod 开发(十):制作一把AOE武器

饥荒Mod 开发(九)&#xff1a;物品栏排列 饥荒Mod 开发(十一)&#xff1a;修改物品堆叠 前面的文章介绍了很多基础知识以及如何制作一个物品&#xff0c;这次制作一把武器&#xff0c;装备之后可以用来攻击怪物。 制作武器贴图和动画 1.1 制作贴图。 先准备一张武器的贴图&a…...

微服务实战系列之ZooKeeper(下)

前言 通过前序两篇关于ZooKeeper的介绍和总结&#xff0c;我们可以大致理解了它是什么&#xff0c;它有哪些重要组成部分。 今天&#xff0c;博主特别介绍一下ZooKeeper的一个核心应用场景&#xff1a;分布式锁。 应用ZooKeeper Q&#xff1a;什么是分布式锁 首先了解一下&…...

FFmpeg项目的组成

主要由三个部分组成&#xff1a; 工具 ffmpeg&#xff1a;用于音视频转码、转换ffplay&#xff1a;音视频播放器ffserver&#xff1a;流媒体服务器ffprobe&#xff1a;多媒体码流分析器 SDK 这个部分是供开发者使用的SDK&#xff0c;SDK是编译好的库。基本上每个平台都有对…...

计算机网络:数据链路层(广域网、PPP协议、HDLC协议)

今天又学会了一个知识&#xff0c;加油&#xff01; 目录 一、广域网 二、PPP协议 1、PPP协议应满足的要求 2、PPP协议无需满足的要求 3、PPP协议的三个组成部分 4、PPP协议的状态图 5、PPP协议的帧格式 三、HDLC协议 1、HDLC的站&#xff08;主站、从站、复合站&…...

Spring Boot i18n中文文档

本文为官方文档直译版本。原文链接 Spring Boot 支持本地化消息&#xff0c;因此您的应用程序可以满足不同语言偏好的用户。默认情况下&#xff0c;Spring Boot 会在类路径的根目录下查找是否存在消息资源包。 自动配置适用于已配置资源包的默认属性文件&#xff08;默认为 mes…...

持久化存储 StorageClass

kubernetes从v1.4版本开始引入了一个新的资源对象StorageClass&#xff0c;用于标记存储资源的特性和性能。到v1.6版本时&#xff0c;StorageClass和动态资源供应的机制得到了完善&#xff0c;实现了存储卷的按需创建&#xff0c;在共享存储的自动化管理进程中能够实现了重要的…...

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】郭老二博文之&#xff1a;C目录 1、XML文件格式简介 1&#xff09;XML文件的开头一般都有个声明&#xff0c;声明是可选 <&#xff1f;xml version"1.0" encoding"UTF-8"?>2&#xff09;根元素&#xff1a;XML文件最外层的元素 3&#xff…...

京东体育用品销售数据分析与可视化系统

京东体育用品销售数据分析与可视化系统 前言数据爬取模块1. 数据爬取2. 数据处理3. 数据存储 数据可视化模块1. 数据查看2. 店铺商品数量排行3. 整体好评率4. 不同品牌市场占比5. 品牌差评率排名6. 品牌价格排名7. 品牌评论数量分布 创新点 前言 在体育用品行业&#xff0c;了…...

【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. 中断系统 中断&#xff1a;在主程序运行过程中&#xff0…...

CSS BFC详解

概念 BFC&#xff08;Block Formatting Context&#xff09;是CSS中的一个概念&#xff0c;用于描述一个独立的渲染区域&#xff0c;其中的元素按照一定规则进行布局和渲染。 BFC具有以下特性和作用 清除浮动&#xff1a;当一个元素的float属性设置为left或right时&#xff…...

【C语言】实战项目——通讯录

引言 学会创建一个通讯录&#xff0c;对过往知识进行加深和巩固。 文章很长&#xff0c;要耐心学完哦&#xff01; ✨ 猪巴戒&#xff1a;个人主页✨ 所属专栏&#xff1a;《C语言进阶》 &#x1f388;跟着猪巴戒&#xff0c;一起学习C语言&#x1f388; 目录 引言 实战 建…...

05 Rust 结构体

结构体 Rust 中的结构体&#xff08;Struct&#xff09;与元组&#xff08;Tuple&#xff09;都可以将若干个类型不一定相同的数据捆绑在一起形成整体&#xff0c;但结构体的每个成员和其本身都有一个名字&#xff0c;这样访问它成员的时候就不用记住下标了。 元组常用于非定…...

C语言预处理详解及其指令

预处理详解 1.预定义符号2.#define定义常量基本使用方法举例子如果在define定义的表示符后面加上分号会发生什么&#xff1f;用一下来解释 3. #define定义宏举例例1例2 4. 带有副作用的宏参数例如: 5. 宏替换的规则6. 宏函数的对比宏和函数的一个对比 7. #和##7.1 #运算符7.2 #…...

【数据结构—队列的实现】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一、队列 1.1队列的概念及结构 二、队列的实现 2.1头文件的实现—Queue.h 2.2源文件的实现—Queue.c 2.3源文件的测试—test.c 三、测试队列实际数据的展示 3.…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用&#xff0c;结合SQLite数据库实现联系人管理功能&#xff0c;并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能&#xff0c;同时可以最小化到系统…...