RabbitMQ实现订单30分钟超时自动关闭

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: RabbitMQ实现订单30分钟超时自动关闭

订单30分钟未支付,系统自动超时关闭有哪些实现方案?

1.基于任务调度实现,效率是非常低,耗服务器性能

2.基于redis过期key实现.用户下单的时候,生成一个令牌(有效期)30分钟,存放到我们redis;

redis.set(orderToken ,orderID) 下单时候存放到redis,并存储id入库,30分钟过期,

redis客户端监听,过期获取到orderId,拿orderId去查订单,没有支付则,订单关闭,库存增加

缺点:非常冗余 ,会在表中存放一个冗余字段

3.基于redis延迟队列   https://blog.csdn.net/zhangshengqiang168/article/details/100130523
4.基于MQ的延迟队列实现(最佳)    死信队列(延迟队列)
    原理:当我们在下单的时候,往MQ投递一个消息设置有效期为30分钟,但该消息失效的时候(没有被消费的情况下),
         执行我们客户端一个方法告诉我们该消息已经失效,这时候查询这笔订单是否有支付.

实现原理(消息过期投递到死信队列)

原理:下单投放消息到A交换机(过期时间30分钟),消息到aa队列(绑定死信交换机),不设置aa队列的消费者(故此消息一直未消费).

30分钟后,过期消息投递到死信交换机,死信队列,由死信消费者消费,判断订单id是否支付,执行业务逻辑,支付->return

未支付->关闭订单,返还库存

DEMO演示:

1.声明订单和死信交换机、 订单和死信队列 、关键步骤订单队列绑定订单交换机和死信交换机 、死信队列绑定死信交换机

public class DeadLetterMQConfig {
    /**
     * 订单交换机
     */
    @Value("${mayikt.order.exchange}")
    private String orderExchange;
    /**
     * 订单队列
     */
    @Value("${mayikt.order.queue}")
    private String orderQueue;
    /**
     * 订单路由key
     */
    @Value("${mayikt.order.routingKey}")
    private String orderRoutingKey;
    /**
     * 死信交换机
     */
    @Value("${mayikt.dlx.exchange}")
    private String dlxExchange;
    /**
     * 死信队列
     */
    @Value("${mayikt.dlx.queue}")
    private String dlxQueue;
    /**
     * 死信路由
     */
    @Value("${mayikt.dlx.routingKey}")
    private String dlxRoutingKey;
    /**
     * 声明死信交换机
     */
    @Bean
    public DirectExchange dlxExchange() {
        return new DirectExchange(dlxExchange);
    }
    /**
     * 声明死信队列
     */
    @Bean
    public Queue dlxQueue() {
        return new Queue(dlxQueue);
    }
    /**
     * 声明订单业务交换机
     */
    @Bean
    public DirectExchange orderExchange() {
        return new DirectExchange(orderExchange);
    }
    /**
     * 声明订单队列 核心操作一
     */
    @Bean
    public Queue orderQueue() {
        Map<String, Object> arguments = new HashMap<>(2);
        // 绑定我们的死信交换机
        arguments.put("x-dead-letter-exchange", dlxExchange);
        // 绑定我们的路由key
        arguments.put("x-dead-letter-routing-key", dlxRoutingKey);
        return new Queue(orderQueue, true, false, false, arguments);
    }
    /**
     * 绑定订单队列到订单交换机
     */
    @Bean
    public Binding orderBinding() {
        return BindingBuilder.bind(orderQueue()).to(orderExchange()).with(orderRoutingKey);
    }
    /**
     * 绑定死信队列到死信交换机
     */
    @Bean
    public Binding binding() {
        return BindingBuilder.bind(dlxQueue()).to(dlxExchange()).with(dlxRoutingKey);
    }
}

2.application.properties文件配置 交换机队列相关名称和其他配置

server.port=8082
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#开启驼峰命名  譬如数据库create_time 自动映射pojo属性createTime
mybatis.configuration.map-underscore-to-camel-case=true
#配置virtual-host虚拟主机
spring.rabbitmq.virtual-host=/zhang_rabbit
#ip地址
spring.rabbitmq.host=127.0.0.1
#用户名  密码
spring.rabbitmq.username=zhang
spring.rabbitmq.password=zhang
#连接端口号
spring.rabbitmq.port=5672
#spring.rabbitmq.publisher-confirm-type=
#模拟演示死信队列
mayikt.dlx.exchange=mayikt_order_dlx_exchange
mayikt.dlx.queue=mayikt_order_dlx_queue
mayikt.dlx.routingKey=dlx
##备胎交换机
mayikt.order.exchange=mayikt_order_exchange
mayikt.order.queue=mayikt_order_queue
mayikt.order.routingKey=mayikt.order

3.创建producer生产者.暂时设置消息10秒过期,验证消息是否加入死信队列

@RestController
public class OrderController {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Value("${mayikt.order.exchange}")
    private String orderExchange; //订单交换机
    @Value("${mayikt.order.routingKey}")
    private String orderRoutingKey; //订单路由key
    @GetMapping("/addOrder")
    public String addOrder(){
        String orderId=System.currentTimeMillis()+"";
        OrderEntity orderEntity=new OrderEntity("订单30分钟过期",orderId,0);
        //订单入库
        int result= orderMapper.addOrder(orderEntity);
        if(result<=0){
            return "fail";
        }
        //rabbit投递消息
    rabbitTemplate.convertAndSend(orderExchange,orderRoutingKey,orderId,messagePostProcessor());
        return "success";
    }
    //处理待发送消息
    private MessagePostProcessor messagePostProcessor(){
        return  new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                //设置有效期30分钟
                //message.getMessageProperties().setExpiration("1800000");
                message.getMessageProperties().setExpiration("10000");
                return message;
            }
        };
    }
}

4.死信消费者消费过期消息

@Component //死信队列
public class OrderDlxConsumer {
    @Autowired
    private OrderMapper orderMapper;
    /**
     * 监听我们的死信队列
     */
    @RabbitListener(queues = "mayikt_order_dlx_queue")
    public void orderConsumer(String orderId) {
        System.out.println("死信队列获取消息:" + orderId);
        if (StringUtils.isEmpty(orderId)) {
            return;
        }
        //根据id查询
        OrderEntity orderEntity = orderMapper.getOrder(orderId);
        if (null == orderEntity) {
            return;
        }
        //获取状态
        Integer orderStatus=orderEntity.getOrderStatus();
        //判断未支付 , 关闭订单
        if(0==orderStatus){
            orderMapper.updateStatus(orderId,2);
            //库存返还
        }
    }
}

5.下单测试 http://localhost:8082/addOrder.订单入库成功.状态0未支付

10秒后,死信消费者处理 状态0未支付 变为2 已关闭

相关实践学习
消息队列RocketMQ版:基础消息收发功能体验
本实验场景介绍消息队列RocketMQ版的基础消息收发功能,涵盖实例创建、Topic、Group资源创建以及消息收发体验等基础功能模块。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
相关文章
|
消息中间件 Apache RocketMQ
rocketmq客户端发送消息报错和超时问题
org.apache.rocketmq.remoting.exception.RemotingTimeoutException: wait response on the channel <10.0.21.69:10911> timeout, 1000(ms)、 closeChannel: close the connection to remote address
3503 1
rocketmq客户端发送消息报错和超时问题
|
5月前
|
消息中间件 Java 物联网
消息队列 MQ操作报错合集之建立连接时发生了超时错误,该如何解决
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
消息队列 MQ操作报错合集之建立连接时发生了超时错误,该如何解决
|
消息中间件 Java
springboot RabbitMQ 连接超时配置
springboot RabbitMQ 连接超时配置
481 0
|
7月前
|
物联网
MQTT常见问题之链接超时发送失败如何解决
MQTT(Message Queuing Telemetry Transport)是一个轻量级的、基于发布/订阅模式的消息协议,广泛用于物联网(IoT)中设备间的通信。以下是MQTT使用过程中可能遇到的一些常见问题及其答案的汇总:
|
消息中间件 Java
Springboot与RabbitMQ消息超时时间、队列消息超时时间
Springboot与RabbitMQ消息超时时间、队列消息超时时间
376 0
|
消息中间件 NoSQL Java
使用RabbitMQ实现未支付订单在30分钟后自动过期
使用RabbitMQ实现未支付订单在30分钟后自动过期
使用RabbitMQ实现未支付订单在30分钟后自动过期
|
消息中间件 NoSQL Java
RabbitMQ死信队列实战——解决订单超时未支付
RabbitMQ死信队列实战——解决订单超时未支付
724 0
RabbitMQ死信队列实战——解决订单超时未支付
|
消息中间件 Java Kafka
从零搭建基于SpringBoot的秒杀系统(六):使用RabbitMQ让订单指定时间后失效
消息中间件属于分布式系统中一个子系统,关注于数据的发送和接收,利用高效可靠的异步消息传递机制对分布式系统中的其余各个子系统进行集成。java中常用的消息中间件有ActiveMQ、RabbitMQ、Kafka等等。消息中间件的作用主要有系统解耦、异步调用、流量削峰等等。
从零搭建基于SpringBoot的秒杀系统(六):使用RabbitMQ让订单指定时间后失效
|
网络协议 算法 网络性能优化
我的mqtt协议和emqttd开源项目个人理解(15) - MQTT消息推送协议应用数据包超时是否需要重发?
我的mqtt协议和emqttd开源项目个人理解(15) - MQTT消息推送协议应用数据包超时是否需要重发?
363 0
|
消息中间件 Java
Java秒杀系统实战系列~RabbitMQ死信队列处理超时未支付的订单
摘要:本篇博文是“Java秒杀系统实战系列文章”的第十篇,本篇博文我们将采用RabbitMQ的死信队列的方式处理“用户秒杀成功生成订单后,却迟迟没有支付”的情况,一起来见识一下RabbitMQ死信队列在实际业务环境下的强大之处! 内容:对于消息中间件RabbitMQ,Debug其实在前面的篇章中已...
7742 0