springboot整合rabbitmq实现订单超时取消案例分析

 更新时间:2025年01月26日 10:29:52   作者:百万彩票中奖候选人  
本文介绍了如何使用SpringBoot和RabbitMQ实现订单超时取消功能,通过配置TTL队列和死信交换机,可以管理订单的超时逻辑,实际应用中,可以通过数据库标记订单状态或手动确认机制来防止订单被错误取消

订单超时取消案例,详细请往下看~~~

1. RabbitMQ 配置类

RabbitMQConfig.java

这个类负责定义RabbitMQ的交换机、队列和绑定配置。

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

@Configuration
public class RabbitMQConfig {

    public static final String ORDER_EXCHANGE = "order-exchange";
    public static final String ORDER_QUEUE = "order-queue";
    public static final String ORDER_ROUTING_KEY = "order-routing-key";

    public static final String TTL_ORDER_QUEUE = "ttl-order-queue";
    public static final String TTL_ORDER_ROUTING_KEY = "ttl-order-routing-key";

    // 定义一个Direct类型的交换机
    @Bean
    public DirectExchange orderExchange() {
        return new DirectExchange(ORDER_EXCHANGE);
    }

    // 定义一个普通的队列,用于接收实际订单处理的消息
    @Bean
    public Queue orderQueue() {
        return QueueBuilder.durable(ORDER_QUEUE).build();
    }

    // 定义一个TTL(时间到期)队列,消息会在这个队列中等待TTL后转发到实际处理队列
    @Bean
    public Queue ttlOrderQueue() {
        return QueueBuilder.durable(TTL_ORDER_QUEUE)
                           .withArgument("x-dead-letter-exchange", ORDER_EXCHANGE) // 设置死信交换机
                           .withArgument("x-dead-letter-routing-key", ORDER_ROUTING_KEY) // 设置死信路由键
                           .withArgument("x-message-ttl", 60000) // 设置TTL为60秒
                           .build();
    }

    // 将实际处理队列绑定到交换机
    @Bean
    public Binding orderBinding() {
        return BindingBuilder.bind(orderQueue()).to(orderExchange()).with(ORDER_ROUTING_KEY);
    }

    // 将TTL队列绑定到交换机
    @Bean
    public Binding ttlOrderBinding() {
        return BindingBuilder.bind(ttlOrderQueue()).to(orderExchange()).with(TTL_ORDER_ROUTING_KEY);
    }
}

详细解释:

交换机(Exchange)

  • orderExchange:定义了一个DirectExchange类型的交换机order-exchange
  • Direct类型的交换机会根据路由键(routing key)精确匹配消息队列。

队列(Queue)

  • orderQueue:定义了一个普通的队列order-queue,这个队列用于接收和处理订单消息。
  • ttlOrderQueue:定义了一个TTL队列ttl-order-queue,这个队列设置了TTL(x-message-ttl)为60秒。当消息在这个队列中超过60秒未被消费,它会变成死信消息(Dead Letter),然后根据配置的死信交换机(x-dead-letter-exchange)和死信路由键(x-dead-letter-routing-key)转发到指定的队列。

绑定(Binding)

  • orderBinding:将order-queue队列绑定到order-exchange交换机,使用路由键order-routing-key
  • ttlOrderBinding:将ttl-order-queue队列绑定到order-exchange交换机,使用路由键ttl-order-routing-key。这意味着发送到这个路由键的消息会首先进入TTL队列。

2. 订单服务

OrderService.java

这个类负责订单的创建和付款逻辑。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.UUID;

@Service
public class OrderService {

    private static final Logger logger = LoggerFactory.getLogger(OrderService.class);

    @Autowired
    private AmqpTemplate amqpTemplate;

    // 创建订单并发送消息到TTL队列
    public void createOrder(String orderId) {
        logger.info("创建订单: {}", orderId);
        amqpTemplate.convertAndSend(RabbitMQConfig.ORDER_EXCHANGE, RabbitMQConfig.TTL_ORDER_ROUTING_KEY, orderId);
    }

    // 支付订单
    public void payOrder(String orderId) {
        logger.info("支付订单: {}", orderId);
        // 订单支付逻辑
        // 支付成功后,需要取消TTL队列中的消息,防止订单被取消
        // 可以通过业务逻辑来实现,比如数据库状态变化
    }
}

详细解释:

  • createOrder方法:当创建一个订单时,会生成一个唯一的订单ID,并将其发送到order-exchange交换机,使用ttl-order-routing-key路由键。这会将消息放入TTL队列ttl-order-queue
  • payOrder方法:模拟支付订单的过程。支付成功后,需要在业务逻辑中处理,确保订单不会被超时取消。这个例子没有实现具体的取消逻辑,但在实际应用中,可以通过数据库或其他机制来实现。

3. 超时监听器

OrderTimeoutListener.java

这个类负责监听超时消息并取消订单。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class OrderTimeoutListener {

    private static final Logger logger = LoggerFactory.getLogger(OrderTimeoutListener.class);

    // 监听来自order-queue队列的消息
    @RabbitListener(queues = RabbitMQConfig.ORDER_QUEUE)
    public void handleOrderTimeout(String orderId) {
        logger.info("订单超时未支付,取消订单: {}", orderId);
        // 取消订单的业务逻辑
    }
}

详细解释:

  • handleOrderTimeout方法:监听order-queue队列中的消息。
  • 当TTL时间到期后,消息会被转发到这个队列,然后这个方法会被触发,处理订单超时取消的业务逻辑。

4. 主应用程序

RabbitMqOrderApplication.java

这个类是Spring Boot的主应用程序类,包含了启动逻辑和示例订单处理流程。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RabbitMqOrderApplication implements CommandLineRunner {

    @Autowired
    private OrderService orderService;

    public static void main(String[] args) {
        SpringApplication.run(RabbitMqOrderApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        String orderId = UUID.randomUUID().toString();
        orderService.createOrder(orderId);
        
        // 模拟延迟支付
        Thread.sleep(30000); // 30秒后支付
        orderService.payOrder(orderId);
    }
}

详细解释:

  • CommandLineRunner接口:实现了这个接口的run方法会在Spring Boot应用启动后立即执行。
  • run方法:生成一个唯一的订单ID,调用orderService.createOrder方法创建订单,并将订单消息发送到TTL队列。然后,模拟延迟30秒后调用orderService.payOrder方法支付订单。

总结

在这个示例中,我们展示了如何使用Spring Boot和RabbitMQ实现一个简单的订单超时取消功能。通过配置TTL队列和死信交换机,可以有效地管理订单的超时逻辑。

实际应用中,可以根据具体需求调整TTL时间和业务逻辑处理订单状态。

在支付成功后需要取消TTL队列中的消息,防止订单被取消,可以通过以下几种方法来实现:

方法一:使用数据库标记和业务逻辑过滤

1.数据库标记订单状态

  • 在订单数据库中添加一个字段来标记订单状态,例如status字段,状态值可以是NEWPAIDCANCELLED等。
  • 当订单支付成功后,将订单状态更新为PAID

2.业务逻辑过滤

  • 在处理超时消息时,首先检查订单的状态,如果订单已经支付(状态为PAID),则忽略取消操作。

方法二:使用消息确认机制(ACK/NACK)

手动ACK消息

  • 配置RabbitMQ的消息监听器,使其使用手动确认(ACK)模式。
  • 当订单支付成功时,通过业务逻辑显式地确认消息,这样RabbitMQ就不会将消息重新发送。

具体实现示例

下面我们详细介绍如何实现这两种方法。

方法一:使用数据库标记和业务逻辑过滤

1. 修改订单服务

  • OrderService.java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    private static final Logger logger = LoggerFactory.getLogger(OrderService.class);

    @Autowired
    private AmqpTemplate amqpTemplate;

    @Autowired
    private OrderRepository orderRepository;

    // 创建订单并发送消息到TTL队列
    public void createOrder(String orderId) {
        Order order = new Order(orderId, "NEW");
        orderRepository.save(order);

        logger.info("创建订单: {}", orderId);
        amqpTemplate.convertAndSend(RabbitMQConfig.ORDER_EXCHANGE, RabbitMQConfig.TTL_ORDER_ROUTING_KEY, orderId);
    }

    // 支付订单
    public void payOrder(String orderId) {
        Order order = orderRepository.findById(orderId).orElse(null);
        if (order != null && "NEW".equals(order.getStatus())) {
            order.setStatus("PAID");
            orderRepository.save(order);

            logger.info("支付订单: {}", orderId);
        }
    }
}

2. 修改订单超时监听器

  • OrderTimeoutListener.java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class OrderTimeoutListener {

    private static final Logger logger = LoggerFactory.getLogger(OrderTimeoutListener.class);

    @Autowired
    private OrderRepository orderRepository;

    // 监听来自order-queue队列的消息
    @RabbitListener(queues = RabbitMQConfig.ORDER_QUEUE)
    public void handleOrderTimeout(String orderId) {
        Order order = orderRepository.findById(orderId).orElse(null);
        if (order != null && "NEW".equals(order.getStatus())) {
            order.setStatus("CANCELLED");
            orderRepository.save(order);

            logger.info("订单超时未支付,取消订单: {}", orderId);
        } else {
            logger.info("订单已经处理: {}", orderId);
        }
    }
}

3. 订单实体和仓库

  • Order.java
import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Order {

    @Id
    private String id;
    private String status;

    // getters and setters
    public Order() {
    }

    public Order(String id, String status) {
        this.id = id;
        this.status = status;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }
}
  • OrderRepository.java
import org.springframework.data.repository.CrudRepository;

public interface OrderRepository extends CrudRepository<Order, String> {
}

方法二:使用消息确认机制(ACK/NACK)

1. 配置消息监听器为手动确认模式

  • RabbitMQConfig.java
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitMQConfig {

    // 其他配置...

    @Bean
    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setAcknowledgeMode(AcknowledgeMode.MANUAL); // 设置手动确认
        return factory;
    }
}

2. 修改订单超时监听器以手动确认消息

  • OrderTimeoutListener.java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.rabbitmq.client.Channel;

@Component
public class OrderTimeoutListener implements ChannelAwareMessageListener {

    private static final Logger logger = LoggerFactory.getLogger(OrderTimeoutListener.class);

    @Autowired
    private OrderRepository orderRepository;

    @Override
    @RabbitListener(queues = RabbitMQConfig.ORDER_QUEUE)
    public void onMessage(Message message, Channel channel) throws Exception {
        String orderId = new String(message.getBody());
        Order order = orderRepository.findById(orderId).orElse(null);
        if (order != null && "NEW".equals(order.getStatus())) {
            order.setStatus("CANCELLED");
            orderRepository.save(order);
            logger.info("订单超时未支付,取消订单: {}", orderId);
        } else {
            logger.info("订单已经处理: {}", orderId);
        }

        // 手动确认消息
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }
}

通过这种方式,我们可以在订单支付成功后,通过数据库标记或手动确认机制,确保消息不会被重新发送或处理,从而防止订单被错误地取消。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 深入理解equals和hashCode方法

    深入理解equals和hashCode方法

    在Java中,equals和hashCode方法是Object中提供的两个方法,这两个方法对以后的学习有很大的帮助,本文就深度来去讲解这两个方法。下面小编带大家来一起学习吧
    2019-06-06
  • Json字符串转Java对象和List代码实例

    Json字符串转Java对象和List代码实例

    这篇文章主要介绍了Json字符串转Java对象和List代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06
  • LinkedBlockingQueue链式阻塞队列的使用和原理解析

    LinkedBlockingQueue链式阻塞队列的使用和原理解析

    这篇文章主要介绍了LinkedBlockingQueue链式阻塞队列的使用和原理解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10
  • Java链表(Linked List)基本原理与实现方法入门示例

    Java链表(Linked List)基本原理与实现方法入门示例

    这篇文章主要介绍了Java链表(Linked List)基本原理与实现方法,结合实例形式分析了Java链表(Linked List)的功能、原理、实现方法与操作注意事项,需要的朋友可以参考下
    2020-03-03
  • Java去重排序之Comparable与Comparator的使用及说明

    Java去重排序之Comparable与Comparator的使用及说明

    这篇文章主要介绍了Java去重排序之Comparable与Comparator的使用及说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • Java获取指定父节点、子节点的方法实现

    Java获取指定父节点、子节点的方法实现

    在Java中,要获取指定节点的父节点和子节点,通常需要使用 DOM,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2024-02-02
  • SpringBoot2零基础到精通之JUnit 5与指标监控

    SpringBoot2零基础到精通之JUnit 5与指标监控

    SpringBoot是一种整合Spring技术栈的方式(或者说是框架),同时也是简化Spring的一种快速开发的脚手架,本篇让我们一起学习JUnit 5与指标监控
    2022-03-03
  • Spring Cloud下基于OAUTH2认证授权的实现示例

    Spring Cloud下基于OAUTH2认证授权的实现示例

    这篇文章主要介绍了Spring Cloud下基于OAUTH2认证授权的实现示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-03-03
  • SSM框架下实现登录注册的示例代码

    SSM框架下实现登录注册的示例代码

    这篇文章主要介绍了SSM框架下实现登录注册的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • 使用React和Java实现文本摘要小工具

    使用React和Java实现文本摘要小工具

    本文将详细介绍如何使用 React 和 Java 搭建一个小型文本摘要工具,并基于 Hugging Face 提供的 API 来实现智能摘要功能,感兴趣的可以了解下
    2024-11-11

最新评论