SpringBoot系列之RabbitMQ 实现订单超时未支付自动关闭功能

news2025/6/6 5:08:53

系列博客专栏:

  • JVM系列博客专栏
  • SpringBoot系列博客

RabbitMQ 实现订单超时自动关闭功能:从原理到实践的全流程解析


一、业务场景与技术选型

在电商系统中,订单超时未支付自动关闭功能是保障库存准确性、提升用户体验的核心机制。传统定时任务扫描数据库的方案存在实时性差、性能损耗高等问题。

基于 RabbitMQ 的延迟消息方案优势

  • 通过DLX队列和消息 TTL 实现精准延迟
  • 提供可靠消息传递机制,支持消息持久化与消费确认
  • 与 Spring Boot 生产力生态深度集成,开发体验友好

技术选型对比

方案实时性性能损耗实现复杂度可扩展性
定时任务轮询数据库
Redis ZSet
RabbitMQ 延迟队列

二、项目环境搭建详解

2.1 依赖配置深度解析

<dependencies>
    <!-- 核心依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
    
    <!-- 数据持久化 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    
    <!-- 幂等性控制 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
    <!-- 开发效率 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

2.2 配置文件最佳实践

spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: admin
    password: 123456
    virtual-host: /
    listener:
      simple:
        acknowledge-mode: manual  # 手动确认模式保障可靠性
        prefetch: 10              # 消费者预取策略优化性能
        concurrency: 3
        max-concurrency: 10

三、核心业务逻辑设计

3.1 状态机设计:订单状态流转图

支付成功
超时未支付
未支付
已支付
已关闭
结束

3.2 RabbitMQ 架构设计

3.2.1 交换机与队列拓扑图
消费者
延迟消息处理
生产者
order.routing.key
x-dead-letter-exchange
order.dlx.routing.key
OrderDelayConsumer
order.delay.queue
order.dlx.exchange
order.process.queue
order.exchange
订单服务
3.2.2 关键配置代码
package com.example.springboot.rabbitmq.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class RabbitMQConfig {

    public static final String ORDER_EXCHANGE = "order.exchange";
    public static final String ORDER_PROCESS_QUEUE = "order.process.queue";
    public static final String ORDER_ROUTING_KEY = "order.routing.key";

    public static final String ORDER_DLX_EXCHANGE = "order.dlx.exchange";
    public static final String ORDER_DELAY_QUEUE = "order.delay.queue";
    public static final String ORDER_DLX_ROUTING_KEY = "order.dlx.routing.key";

    // 设置订单交换机类
    @Bean
    public DirectExchange orderExchange() {
        return new DirectExchange(ORDER_EXCHANGE, true, false);
    }

    // 配置处理队列,设置TTL和DLX交换机
    @Bean
    public Queue orderProcessQueue() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-dead-letter-exchange", ORDER_DLX_EXCHANGE); // 死信交换机
        args.put("x-message-ttl", 60000); // 设置消息过期时间(毫秒)
        return new Queue(ORDER_PROCESS_QUEUE, true, false, false, args);
    }

    // 配置延迟队列
    @Bean
    public Queue orderDelayQueue() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-dead-letter-exchange", ORDER_DLX_EXCHANGE);
        args.put("x-dead-letter-routing-key", ORDER_DLX_ROUTING_KEY);
        args.put("x-max-priority", 10); // 设置队列优先级
        args.put("x-message-ttl", 60000); // 设置消息过期时间(毫秒)
        return new Queue(ORDER_DELAY_QUEUE, true, false, false, args);
    }

    // 绑定延迟队列到订单交换机
    @Bean
    public Binding delayBinding() {
        return BindingBuilder.bind(orderDelayQueue()).to(orderExchange()).with(ORDER_ROUTING_KEY);
    }

    // 配置DLX交换机
    @Bean
    public DirectExchange orderDlxExchange() {
        return new DirectExchange(ORDER_DLX_EXCHANGE, true, false);
    }

    // 绑定处理队列到DLX交换机
    @Bean
    public Binding processQueueBinding() {
        return BindingBuilder.bind(orderProcessQueue()).to(orderDlxExchange()).with(ORDER_DLX_ROUTING_KEY);
    }

}

3.3 订单服务核心实现

3.3.1 幂等性控制
@Service
public class OrderServiceImpl {
    @Autowired
    private OrderRepository orderRepository;    

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    @Transactional
    public Order createOrder(OrderDTO orderDto) {
        // 幂等性校验
        if (redisTemplate.hasKey("ORDER_CREATE_" + orderDto.getRequestId())) {
            throw new IllegalArgumentException("重复请求");
        }
        redisTemplate.opsForValue().set("ORDER_CREATE_" + orderDto.getRequestId(), "1", 5, TimeUnit.MINUTES);


        // 设置订单初始状态为未支付
        Order order = new Order();
        order.setOrderId(UUID.randomUUID().toString());
        order.setStatus(OrderStatus.UNPAID.getCode());
        order.setCreateTime(LocalDateTime.now());
        order.setUpdateTime(LocalDateTime.now());
        order.setUserId(orderDto.getUserId());
        order.setAmount(orderDto.getAmount());

        // 保存订单到数据库
        Order savedOrder = orderRepository.save(order);
        log.info("订单创建成功,订单ID:{}", savedOrder.getOrderId());

        // 发送订单到延迟队列,设置延迟30分钟
        long delayTime = 30 * 60 * 1000; // 30分钟
        sendOrderToDelayQueue(savedOrder.getOrderId(), delayTime);

        return savedOrder;
    }
}
3.3.2 消息可靠性保障
@Override
public void sendOrderToDelayQueue(String orderId, long delayTime) {
    rabbitTemplate.convertAndSend(
            RabbitMQConfig.ORDER_EXCHANGE,
            RabbitMQConfig.ORDER_ROUTING_KEY,
            orderId,
            message -> {
                message.getMessageProperties().setExpiration(String.valueOf(delayTime));
                return message;
            }
    );

    rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
        if (!ack) {
            // 消息发送失败,执行数据库回滚或补偿逻辑
            orderRepository.deleteById(orderId);
            log.error("消息发送失败,原因:{}", cause);
        }
    });

    log.info("订单已发送到延迟队列,订单ID:{},延迟时间:{}毫秒", orderId, delayTime);
}

四、高可用与性能优化

4.1 RabbitMQ 集群配置建议

配置项生产环境建议值说明
节点数3节点(奇数)基于仲裁队列实现高可用性
持久化策略全部消息持久化确保重启后消息不丢失
镜像队列开启(同步到所有节点)提升容灾能力
内存水位线0.4超过时触发内存换页

4.2 性能调优参数

spring:
  rabbitmq:
    listener:
      simple:
        prefetch: 10             # 消费者预取策略
        default-requeue-rejected: false  # 拒
        绝消息不重新入队
    template:
      retry:
        enabled: true
        initial-interval: 100ms
        max-attempts: 3

五、功能测试与监控体系

5.1 自动化测试用例

@SpringBootTest
public class OrderServiceTest {
    
    @Test
    void testOrderTimeoutClose() throws InterruptedException {
        // 创建订单
        Order order = orderService.createOrder(new OrderDTO());
        
        // 验证消息发送
        assertEquals(1, rabbitTemplate.getMessageCount(RabbitMQConfig.ORDER_DELAY_QUEUE));
        
        // 模拟延迟
        Thread.sleep(31 * 60 * 1000);
        
        // 验证订单状态
        assertEquals(OrderStatus.CLOSED, orderService.getOrderById(order.getId()).getStatus());
    }
}

5.2 监控指标采集

@Bean
public CollectorRegistry rabbitMQMetrics() {
    return new RabbitMQMetricsCollector(
        rabbitConnectionFactory,
        List.of("order.delay.queue", "order.process.queue")
    );
}

采集指标:

  • queue.message.count:队列当前消息数
  • queue.message.age:消息平均年龄
  • consumer.process.rate:消费者处理速率
  • order.closed.total:订单关闭总数

六、常见问题与解决方案

6.1 消息丢失问题

生产者保障

rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
    if (!ack) {
        // 补偿逻辑:记录未确认消息,定期重试
    }
});

消费者保障

@RabbitListener(queues = "order.process.queue")
public void processOrder(String orderId, Channel channel, Message message) {
    try {
        // 业务逻辑...
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    } catch (Exception e) {
        channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
    }
}

6.2 并发更新问题

乐观锁解决方案

@Transactional
public void updateOrderStatus(String orderId) {
    Order order = orderRepository.findByOrderIdAndVersion(
        orderId, expectedVersion
    ).orElseThrow(() -> new BusinessException("订单状态已变更"));
    
    order.setStatus(newStatus);
    order.setVersion(order.getVersion() + 1);
    orderRepository.save(order);
}

七、扩展场景与最佳实践

7.1 动态延迟时间

public void createOrder(Order order) {
    int delayMinutes = getOrderDelayTime(order.getType());
    sendOrderToDelayQueue(order.getId(), delayMinutes * 60 * 1000);
}

7.2 分布式事务支持

结合 Seata 实现最终一致性:

@GlobalTransactional
public void createOrderWithStock(Order order) {
    stockService.decreaseStock(order.getProductId(), order.getQuantity());
    orderService.createOrder(order);
}

八、总结与最佳实践清单

维度最佳实践要点
可靠性启用消息持久化、手动确认、发布确认机制,构建消息补偿机制
性能使用消费者预取、合理设置 TTL,避免队列积压
可观测性采集队列指标、业务日志,集成 Prometheus + Grafana 监控
扩展性设计可配置的延迟策略,支持动态路由键
安全性使用虚拟主机隔离业务,配置 SSL 加密连接,定期轮换访问凭证

关键成功因素

  1. DLX队列 + 消息 TTL 的正确配置
  2. 手动确认模式的合理使用
  3. 幂等性控制机制的实现
  4. 消费者重试与拒绝策略的设计
  5. 分布式事务的最终一致性保障

通过以上设计,我们构建了一个具备高可靠性、可扩展性和可观测性的订单超时关闭系统。实际应用中可根据业务规模调整 RabbitMQ 集群配置,并通过链路追踪工具(如 SkyWalking)进一步优化全链路性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2398797.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++高级主题】命令空间(五):类、命名空间和作用域

目录 一、实参相关的查找&#xff08;ADL&#xff09;&#xff1a;函数调用的 “智能搜索” 1.1 ADL 的核心规则 1.2 ADL 的触发条件 1.3 ADL 的典型应用场景 1.4 ADL 的潜在风险与规避 二、隐式友元声明&#xff1a;类与命名空间的 “私密通道” 2.1 友元声明的基本规则…

国标GB28181设备管理软件EasyGBS视频平台筑牢文物保护安全防线创新方案

一、方案背景​ 文物作为人类文明的珍贵载体&#xff0c;具有不可再生性。当前&#xff0c;盗窃破坏、游客不文明行为及自然侵蚀威胁文物安全&#xff0c;传统保护手段存在响应滞后、覆盖不全等局限。随着5G与信息技术发展&#xff0c;基于GB28181协议的EasyGBS视频云平台&…

Baklib内容中台AI重构智能服务

AI驱动智能服务进化 在智能服务领域&#xff0c;Baklib内容中台通过自然语言处理技术与深度学习框架的深度融合&#xff0c;构建出具备意图理解能力的知识中枢。系统不仅能够快速解析用户输入的显性需求&#xff0c;更通过上下文关联分析算法识别会话场景中的隐性诉求&#xf…

数据库包括哪些?关系型数据库是什么意思?

目录 一、数据库包括哪些 &#xff08;一&#xff09;关系型数据库 &#xff08;二&#xff09;非关系型数据库 &#xff08;三&#xff09;分布式数据库 &#xff08;四&#xff09;内存数据库 二、关系型数据库是什么 &#xff08;一&#xff09;关系模型的基本概念 …

Python爬虫监控程序设计思路

最近因为爬虫程序太多&#xff0c;想要为Python爬虫设计一个监控程序&#xff0c;主要功能包括一下几种&#xff1a; 1、监控爬虫的运行状态&#xff08;是否在运行、运行时间等&#xff09; 2、监控爬虫的性能&#xff08;如请求频率、响应时间、错误率等&#xff09; 3、资…

【HarmonyOS 5】Laya游戏如何鸿蒙构建发布详解

【HarmonyOS 5】Laya游戏如何鸿蒙构建发布详解 一、前言 LayaAir引擎是国内最强大的全平台引擎之一&#xff0c;当年H5小游戏火的时候&#xff0c;腾讯入股了腊鸭。我还在游戏公司的时候&#xff0c;17年曾经开发使用腊鸭的H5小游戏&#xff0c;很怀念当年和腊鸭同事一起解决…

【鱼皮-用户中心】笔记

任务&#xff1a;完整了解做项目的思路&#xff0c;接触一些企业及的开发技术 title 企业做项目流程需求分析技术选型 计划一一、前端初始化1. **下载node.js**2. **安装yarn**3. **初始化 Ant Design Pro 脚⼿架&#xff08;关于更多可进入官网了解&#xff09;**4. **开启Umi…

交错推理强化学习方法提升医疗大语言模型推理能力的深度分析

核心概念解析 交错推理:灵活多变的思考方式 交错推理(Interleaved Reasoning)是一种在解决复杂问题时,不严格遵循单一、线性推理路径,而是交替、灵活应用多种推理策略的方法。这种思维方式与人类专家在处理复杂医疗问题时的思考模式更为接近,表现为一种动态、适应性强的…

SpringBatch+Mysql+hanlp简版智能搜索

资源条件有限&#xff0c;需要支持智搜的数据量也不大&#xff0c;上es搜索有点大材小用了&#xff0c;只好写个简版mysql的智搜&#xff0c;处理全文搜素&#xff0c;支持拼音搜索&#xff0c;中文分词&#xff0c;自定义分词断词&#xff0c;地图范围搜索&#xff0c;周边搜索…

go语言基础|slice入门

slice slice介绍 slice中文叫切片&#xff0c;是go官方提供的一个可变数组&#xff0c;是一个轻量级的数据结构&#xff0c;功能上和c的vector&#xff0c;Java的ArrayList差不多。 slice和数组是有一些区别的&#xff0c;是为了弥补数组的一些不足而诞生的数据结构。最大的…

使用 HTML + JavaScript 实现可拖拽的任务看板系统

本文将介绍如何使用 HTML、CSS 和 JavaScript 创建一个交互式任务看板系统。该系统支持拖拽任务、添加新任务以及动态创建列,适用于任务管理和团队协作场景。 效果演示 页面结构 HTML 部分主要包含三个默认的任务列(待办、进行中、已完成)和一个用于添加新列的按钮。 <…

统信 UOS 服务器版离线部署 DeepSeek 攻略

日前&#xff0c;DeepSeek 系列模型因拥有“更低的成本、更强的性能、更好的体验”三大核心优势&#xff0c;在全球范围内备受瞩目。 本次&#xff0c;我们为大家提供了在统信 UOS 服务器版 V20&#xff08;AMD64 或 ARM64 架构&#xff09;上本地离线部署 DeepSeek-R1 模型的…

美尔斯通携手北京康复辅具技术中心开展公益活动,科技赋能助力银龄健康管理

2025 年 5 月 30 日&#xff0c;北京美尔斯通科技发展股份有限公司携手北京市康复辅具技术中心&#xff0c;在朝阳区核桃园社区开展 “全国助残日公益服务” 系列活动。活动通过科普讲座、健康检测与科技体验&#xff0c;将听力保健与心脏健康服务送至居民家门口&#xff0c;助…

Redis Stack常见拓展

Redis JSON RedisJSON 是 Redis Stack 提供的模块之一&#xff0c;允许你以 原生 JSON 格式 存储、检索和修改数据。相比传统 Redis Hash&#xff0c;它更适合结构化文档型数据&#xff0c;并支持嵌套结构、高效查询和部分更新。 #设置⼀个JSON数据,其中$表示JSON数据的根节点…

Linux 驱动之设备树

Linux 驱动之设备树 参考视频地址 【北京迅为】嵌入式学习之Linux驱动&#xff08;第七期_设备树_全新升级&#xff09;_基于RK3568_哔哩哔哩_bilibili 本章总领 1.设备树基本知识 什么是设备树&#xff1f; ​ Linux之父Linus Torvalds在2011年3月17日的ARM Linux邮件列表…

12、企业应收账款(AR)全流程解析:从发票开具到回款完成

在商业活动中&#xff0c;现金流如同企业的命脉&#xff0c;而应收管理则是维系这条命脉正常运转的重要保障。许多企业由于对应收账款缺乏有效管理&#xff0c;常常面临资金周转困难的问题。实践证明&#xff0c;建立科学的应收管理体系能够显著提升资金回笼效率&#xff0c;为…

【notepad++】如何设置notepad++背景颜色?

如何设置notepad背景颜色&#xff1f; 设置--语言格式设置 勾选使用全局背景色 例如选择护眼色---80&#xff0c;97&#xff0c;205&#xff1b;

使用 C++/OpenCV 制作跳动的爱心动画

使用 C/OpenCV 制作跳动的爱心动画 本文将引导你如何使用 C 和 OpenCV 库创建一个简单但有趣的跳动爱心动画。我们将通过绘制参数方程定义的爱心形状&#xff0c;并利用正弦函数来模拟心跳的缩放效果。 目录 简介先决条件核心概念 参数方程绘制爱心动画循环模拟心跳效果 代码…

在Oxygen编辑器中使用DeepSeek

罗马尼亚公司研制开发的Oxygen编辑器怎样与国产大模型结合&#xff0c;这是今年我在tcworld大会上给大家的分享&#xff0c;需要ppt的朋友请私信联系 - 1 - Oxygen编辑器中的人工智能助手 Oxygen编辑器是罗马尼亚的Syncro Soft公司开发的一款结构化文档编辑器。 它是用来编写…

一、基础环境配置

一、虚拟机 主&#xff1a;192.168.200.200 从&#xff1a;192.168.200.201 从&#xff1a;192.168.200.202 二、docker docker基础搭建&#xff0c;有不会的自行百度。 1.目录结构 /opt/software&#xff1a;软件包/opt/module&#xff1a;解压包&#xff0c;自定义脚本…