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

Spring Boot 订单超时自动取消的 3 种主流实现方案

Spring Boot 订单超时自动取消的 3 种主流实现方案

关键词:Spring Boot、订单超时、延迟任务、RabbitMQ、Redis、定时任务

在电商、外卖、票务等业务中,“下单后若 30 分钟未支付则自动取消”是一道经典需求。实现方式既要保证 实时性,又要在 高并发 下保持 低成本、高可靠
本文基于 Spring Boot,给出 3 种生产级落地方案,并附完整代码与选型对比,方便快速决策。


一、需求拆解

功能点约束
触发条件创建时间 + 30 min 仍未支付
实时性秒级(理想) / 分钟级(可接受)
幂等重复取消需幂等
高并发峰值 10 w+/日
数据一致性不能漏单、不能错单

二、方案总览

方案核心机制实时性额外组件代码复杂度
① 定时任务@Scheduled + DB 扫描分钟级★☆☆
② 延迟队列RabbitMQ TTL + DLX秒级RabbitMQ★★☆
③ Redis 过期事件Key TTL + Keyspace Notify秒级Redis★★☆

三、方案 1:定时任务(@Scheduled)

1. 思路

周期性扫描订单表,把“创建时间 + 30 min < 当前时间”且状态为 PENDING 的订单置为 CANCELLED

2. 代码实现

@EnableScheduling
@Component
@RequiredArgsConstructor
public class OrderCancelSchedule {private final OrderService orderService;/** 每 30s 跑一次,可根据数据量调整 */@Scheduled(fixedDelay = 30_000)public void cancelUnpaidOrders() {LocalDateTime expirePoint = LocalDateTime.now().minusMinutes(30);List<Long> ids = orderService.findUnpaidBefore(expirePoint);if (!ids.isEmpty()) {int affected = orderService.batchCancel(ids);log.info("自动取消订单 {} 条", affected);}}
}

3. 优化技巧

  • 分页 + 索引
    CREATE INDEX idx_order_status_created ON t_order(status, created_time);
    
  • 分片扫描:按 ID 或时间分片,避免大表锁。
  • 单机多线程@Async("cancelExecutor") + 线程池。

4. 优缺点

  • ✅ 零依赖、实现快
  • ❌ 数据量大时 DB 压力大;实时性受轮询间隔限制

5. 适用场景

日订单 < 1 w,或作为兜底方案。


四、方案 2:RabbitMQ 延迟队列

1. 思路

订单创建后发送一条 30 min TTL 的消息;到期自动路由到消费队列,消费者检查订单状态并取消。

2. 架构图

Producer ──> Delay Exchange (x-delayed-message) ──> 30min TTL ──> Cancel Queue ──> Consumer

3. 代码实现

3.1 声明交换机 & 队列
@Configuration
public class RabbitDelayConfig {@Beanpublic CustomExchange delayExchange() {Map<String, Object> args = Map.of("x-delayed-type", "direct");return new CustomExchange("order.delay", "x-delayed-message", true, false, args);}@Beanpublic Queue cancelQueue() {return QueueBuilder.durable("order.cancel.queue").build();}@Beanpublic Binding binding() {return BindingBuilder.bind(cancelQueue()).to(delayExchange()).with("order.cancel").noargs();}
}
3.2 发送延迟消息
@Service
@RequiredArgsConstructor
public class OrderPublisher {private final RabbitTemplate rabbitTemplate;public void createOrder(Order order) {// 1. 落库orderMapper.insert(order);// 2. 发送延迟消息rabbitTemplate.convertAndSend("order.delay","order.cancel",order.getId(),msg -> {msg.getMessageProperties().setDelay(30 * 60 * 1000); // 30 minreturn msg;});}
}
3.3 消费并取消
@Component
@RabbitListener(queues = "order.cancel.queue")
public class CancelConsumer {private final OrderService orderService;@RabbitHandlerpublic void handle(Long orderId) {Order order = orderService.find(orderId);if (order != null && order.getStatus() == OrderStatus.PENDING) {orderService.cancel(orderId);}}
}

4. 优缺点

  • ✅ 实时性好(秒级);支持分布式;消息持久化
  • ❌ 需要 RabbitMQ;链路更长

5. 适用场景

中高并发,需秒级取消,已用 MQ 或愿意引入 MQ。


五、方案 3:Redis Keyspace 过期事件

1. 思路

order:{id} 作为 key,30 min TTL;Redis 键过期时推送事件;应用监听后取消订单。

2. Redis 配置

# redis.conf
notify-keyspace-events Ex

或 CLI:

CONFIG SET notify-keyspace-events Ex

3. 代码实现

3.1 订单创建时写 Redis
@Service
public class OrderService {private final StringRedisTemplate redisTemplate;public void createOrder(Order order) {orderMapper.insert(order);// value 随意,这里用 idredisTemplate.opsForValue().set("order:" + order.getId(),String.valueOf(order.getId()),Duration.ofMinutes(30));}
}
3.2 监听过期事件
@Configuration
public class RedisListenerConfig {@Beanpublic RedisMessageListenerContainer container(RedisConnectionFactory cf) {RedisMessageListenerContainer container = new RedisMessageListenerContainer();container.setConnectionFactory(cf);container.addMessageListener((message, pattern) -> {String key = message.toString();if (key.startsWith("order:")) {String orderId = key.substring(6);// 幂等取消orderService.cancelIfUnpaid(Long.valueOf(orderId));}},new PatternTopic("__keyevent@*__:expired"));return container;}
}

4. 幂等 & 可靠性

  • 幂等:取消 SQL 加状态条件 WHERE status = PENDING
  • 可靠性:Redis 重启会丢失未过期 key,需 兜底定时任务(方案 1)双保险。

5. 优缺点

  • ✅ 实时性高,组件少
  • ❌ Redis 重启可能丢事件;需处理幂等

6. 适用场景

已用 Redis,订单量中等,能接受极低概率漏单。


六、3 种方案对比与选型

维度定时任务RabbitMQ 延迟队列Redis 过期事件
实时性分钟级秒级秒级
吞吐量
额外组件RabbitMQRedis
可靠性中(需兜底)
实现复杂度★☆☆★★☆★★☆
推荐场景小流量、兜底高并发、已用 MQ已用 Redis、中等并发

建议

  1. 小项目 → 定时任务即可;
  2. 大流量 → 延迟队列;
  3. 已用 Redis → 过期事件 + 定时任务兜底双保险。

七、灰度 & 监控

  • 灰度发布:按用户尾号或城市分批切换方案。
  • 监控指标
    • 取消成功率
    • MQ 消息积压
    • Redis 过期 QPS
    • 定时任务扫描耗时

八、小结

一句话总结
定时任务 简单但慢;延迟队列 实时但重;Redis 过期 轻量但需兜底。

在实际落地中,可以 并行运行 两种方案(如延迟队列 + 兜底定时任务),通过配置开关灵活切换,确保业务永远在线。祝你的订单永不超卖!

http://www.dtcms.com/a/289286.html

相关文章:

  • 将SAC强化学习算法部署到ROS2的完整指南
  • 基于卷积傅里叶分析网络 (CFAN)的心电图分类的统一时频方法
  • 复杂度+包装类型+泛型
  • 全平台爬虫配置流程
  • Spark专栏开篇:它从何而来,为何而生,凭何而强?
  • Java 递归方法详解:从基础语法到实战应用,彻底掌握递归编程思想
  • XSS的介绍
  • 5G NR PDCCH之CRC处理
  • Java 创建线程的方式笔记
  • 【RK3576】【Android14】ADB工具说明与使用
  • 设计模式笔记(1)简单工厂模式
  • 《汇编语言:基于X86处理器》第8章 复习题和练习,编程练习
  • 深度相机的工作模式(以奥比中光深度相机为例)
  • AI开发 | 基于FastAPI+React的流式对话
  • ChatIM项目语音识别安装与使用
  • 论文笔记: Holistic Semantic Representation for Navigational Trajectory Generation
  • 《计算机网络》实验报告四 TCP协议分析
  • 基于FPGA的多级流水线加法器verilog实现,包含testbench测试文件
  • Haproxy算法精简化理解及企业级高功能实战
  • Uniapp 纯前端台球计分器开发指南:能否上架微信小程序 打包成APP?
  • 专题 解空间的一种遍历方式:深度优先(Depth First)
  • 【unitrix】 6.9 减一操作(sub_one.rs)
  • Go语言的函数
  • qcow2磁盘虚拟机的使用
  • Spring Cloud Gateway 电商系统实战指南:架构设计与深度优化
  • Work SSD基础知识
  • 数列-冒泡排序,鸡尾酒排序
  • LINUX(三)文件I/O、对文件打开、读、写、偏移量
  • 什么是 ELK/Grafana
  • Cosmos:构建下一代互联网的“区块链互联网