SpringBoot怎么整合RabbitMQ实现延迟队列

WBOY

WBOY

2023-05-16

1068人浏览

转载

    如何保证消息不丢失

    rabbitmq消息投递路径

    生产者->交换机->队列->消费者

    总的来说分为三个阶段。

    • 1.生产者保证消息投递可靠性。

    • 2.mq内部消息不丢失。

    • 3.消费者消费成功。

    什么是消息投递可靠性

    简单点说就是消息百分百发送到消息队列中。

    我们可以开启confirmCallback

    生产者投递消息后,mq会给生产者一个ack.根据ack,生产者就可以确认这条消息是否发送到mq.

    开启confirmCallback

    修改配置文件

    #NONE:禁用发布确认模式,是默认值,CORRELATED:发布消息成功到交换器后会触发回调方法
    spring:
      rabbitmq:
        publisher-confirm-type: correlated

    测试代码

    @Test  
    public void testConfirmCallback() throws InterruptedException {  
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {  
        /**  
        *  
        * @param correlationData 配置  
        * @param ack 交换机是否收到消息,true是成功,false是失败  
        * @param cause 失败的原因  
        */  
        @Override  
        public void confirm(CorrelationData correlationData, boolean ack, String cause) {  
            System.out.println("confirm=====>");  
            System.out.println("confirm==== ack="+ack);  
            System.out.println("confirm==== cause="+cause);  
            //根据ACK状态做对应的消息更新操作 TODO  
        }  
        });  
        rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME,"ikun.mei", "鸡你太美");  
        Thread.sleep(10000);  
    }

    通过returnCallback保证消息从交换器发送到队列成功。 修改配置文件

    spring:
      rabbitmq:
        #开启returnCallback
        publisher-returns: true
        #交换机处理消息到路由失败,则会返回给生产者
        template:
          mandatory: true

    测试代码

    @Test  
    void testReturnCallback() {  
        //为true,则交换机处理消息到路由失败,则会返回给生产者 配置文件指定,则这里不需指定  
        rabbitTemplate.setMandatory(true);  
        //开启强制消息投递(mandatory为设置为true),但消息未被路由至任何一个queue,则回退一条消息  
        rabbitTemplate.setReturnsCallback(returned -> {  
            int code = returned.getReplyCode();  
            System.out.println("code="+code);  
            System.out.println("returned="+ returned);  
        });  
        rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME,"123456","测试returnCallback");  
    }

    消费者消费消息时需要通过ack手动确认消息已消费。

    修改配置文件

    spring:
      rabbitmq:
        listener:  
          simple:  
            acknowledge-mode: manual

    编写测试代码

    @RabbitHandler  
    public void consumer(String body, Message message, Channel channel) throws IOException {  
        long msgTag = message.getMessageProperties().getDeliveryTag();  
        System.out.println("msgTag="+msgTag);  
        System.out.println("message="+ message);  
        System.out.println("body="+body);  
    
        //成功确认,使用此回执方法后,消息会被 rabbitmq broker 删除  
        channel.basicAck(msgTag,false);  
        // channel.basicNack(msgTag,false,true);  
      
    }

    deliveryTags是消息投递序号,每次消费消息或者消息重新投递后,deliveryTag都会增加

    ttl死信队列

    什么是死信队列

    没有被及时消费的消息存放的队列

    消息有哪几种情况成为死信

    • 消费者拒收消息 (basic.reject/ basic.nack) ,并且没有重新入队 requeue=false

    • 消息在队列中未被消费,且超过队列或者消息本身的过期时间TTL(time-to-live)

    • 队列的消息长度达到极限

    • 结果:消息成为死信后,如果该队列绑定了死信交换机,则消息会被死信交换机重新路由到死信队列

    死信队列经常用来做延迟队列消费。

    延迟队列

    生产者投递到mq中并不希望这条消息立马被消费,而是等待一段时间后再去消费。

    springboot整合rabbitmq实现订单超时自动关闭

    package com.fandf.test.rabbit;  
      
    import org.springframework.amqp.core.*;  
    import org.springframework.beans.factory.annotation.Qualifier;  
    import org.springframework.context.annotation.Bean;  
    import org.springframework.context.annotation.Configuration;  
      
    import java.util.HashMap;  
    import java.util.Map;  
      
    /**  
    * @author fandongfeng  
    * @date 2023/4/15 15:38  
    */  
    @Configuration  
    public class RabbitMQConfig {  
      
        /**  
        * 订单交换机  
        */  
        public static final String ORDER_EXCHANGE = "order_exchange";  
        /**  
        * 订单队列  
        */  
        public static final String ORDER_QUEUE = "order_queue";  
        /**  
        * 订单路由key  
        */  
        public static final String ORDER_QUEUE_ROUTING_KEY = "order.#";  
    
        /**  
        * 死信交换机  
        */  
        public static final String ORDER_DEAD_LETTER_EXCHANGE = "order_dead_letter_exchange";  
        /**  
        * 死信队列 routingKey  
        */  
        public static final String ORDER_DEAD_LETTER_QUEUE_ROUTING_KEY = "order_dead_letter_queue_routing_key";  
    
        /**  
        * 死信队列  
        */  
        public static final String ORDER_DEAD_LETTER_QUEUE = "order_dead_letter_queue";  
    
    
        /**  
        * 创建死信交换机  
        */  
        @Bean("orderDeadLetterExchange")  
        public Exchange orderDeadLetterExchange() {  
            return new TopicExchange(ORDER_DEAD_LETTER_EXCHANGE, true, false);  
        }  
    
        /**  
        * 创建死信队列  
        */  
        @Bean("orderDeadLetterQueue")  
        public Queue orderDeadLetterQueue() {  
            return QueueBuilder.durable(ORDER_DEAD_LETTER_QUEUE).build();  
        }  
    
        /**  
        * 绑定死信交换机和死信队列  
        */  
        @Bean("orderDeadLetterBinding")  
        public Binding orderDeadLetterBinding(@Qualifier("orderDeadLetterQueue") Queue queue, @Qualifier("orderDeadLetterExchange")Exchange exchange) {  
            return BindingBuilder.bind(queue).to(exchange).with(ORDER_DEAD_LETTER_QUEUE_ROUTING_KEY).noargs();  
        }  
    
    
        /**  
        * 创建订单交换机  
        */  
        @Bean("orderExchange")  
        public Exchange orderExchange() {  
            return new TopicExchange(ORDER_EXCHANGE, true, false);  
        }  
    
        /**  
        * 创建订单队列  
        */  
        @Bean("orderQueue")  
        public Queue orderQueue() {  
            Map<string> args = new HashMap(3);  
            //消息过期后,进入到死信交换机  
            args.put("x-dead-letter-exchange", ORDER_DEAD_LETTER_EXCHANGE);  
    
            //消息过期后,进入到死信交换机的路由key  
            args.put("x-dead-letter-routing-key", ORDER_DEAD_LETTER_QUEUE_ROUTING_KEY);  
    
            //过期时间,单位毫秒  
            args.put("x-message-ttl", 10000);  
    
            return QueueBuilder.durable(ORDER_QUEUE).withArguments(args).build();  
        }  
    
        /**  
        * 绑定订单交换机和队列  
        */  
        @Bean("orderBinding")  
        public Binding orderBinding(@Qualifier("orderQueue") Queue queue, @Qualifier("orderExchange")Exchange exchange) {  
            return BindingBuilder.bind(queue).to(exchange).with(ORDER_QUEUE_ROUTING_KEY).noargs();  
        }  
      
      
    }</string>

    消费者

    package com.fandf.test.rabbit;  
      
    import cn.hutool.core.date.DateUtil;  
    import com.rabbitmq.client.Channel;  
    import org.springframework.amqp.core.Message;  
    import org.springframework.amqp.rabbit.annotation.RabbitHandler;  
    import org.springframework.amqp.rabbit.annotation.RabbitListener;  
    import org.springframework.stereotype.Component;  
      
    import java.io.IOException;  
      
    /**  
    * @author fandongfeng  
    * @date 2023/4/15 15:42  
    */  
    @Component  
    @RabbitListener(queues = RabbitMQConfig.ORDER_DEAD_LETTER_QUEUE)  
    public class OrderMQListener {  
      
      
      
        @RabbitHandler  
        public void consumer(String body, Message message, Channel channel) throws IOException {  
            System.out.println("收到消息:" + DateUtil.now());  
            long msgTag = message.getMessageProperties().getDeliveryTag();  
            System.out.println("msgTag=" + msgTag);  
            System.out.println("message=" + message);  
            System.out.println("body=" + body);  
            channel.basicAck(msgTag, false);  
        }  
      
    }

    测试类

    @Test  
    void testOrder() throws InterruptedException {  
    //为true,则交换机处理消息到路由失败,则会返回给生产者 配置文件指定,则这里不需指定  
        rabbitTemplate.setMandatory(true);  
        //开启强制消息投递(mandatory为设置为true),但消息未被路由至任何一个queue,则回退一条消息  
        rabbitTemplate.setReturnsCallback(returned -> {  
        int code = returned.getReplyCode();  
        System.out.println("code=" + code);  
        System.out.println("returned=" + returned);  
        });  
        rabbitTemplate.convertAndSend(RabbitMQConfig.ORDER_EXCHANGE, "order", "测试订单延迟");  
        System.out.println("发送消息:" + DateUtil.now());  
        Thread.sleep(20000);  
    }

    程序输出

    发送消息:2023-04-16 15:14:34
    收到消息:2023-04-16 15:14:44
    msgTag=1
    message=(Body:'测试订单延迟' MessageProperties [headers={spring_listener_return_correlation=03169cfc-5061-41fe-be47-c98e36d17eac, x-first-death-exchange=order_exchange, x-death=[{reason=expired, count=1, exchange=order_exchange, time=Mon Apr 16 15:14:44 CST 2023, routing-keys=[order], queue=order_queue}], x-first-death-reason=expired, x-first-death-queue=order_queue}, contentType=text/plain, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, redelivered=false, receivedExchange=order_dead_letter_exchange, receivedRoutingKey=order_dead_letter_queue_routing_key, deliveryTag=1, consumerTag=amq.ctag-Eh8GMgrsrAH1rvtGj7ykOQ, consumerQueue=order_dead_letter_queue])
    body=测试订单延迟

    相关专题

    更多
    rabbitmq和kafka有什么区别
    rabbitmq和kafka有什么区别

    rabbitmq和kafka的区别:1、语言与平台;2、消息传递模型;3、可靠性;4、性能与吞吐量;5、集群与负载均衡;6、消费模型;7、用途与场景;8、社区与生态系统;9、监控与管理;10、其他特性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

    2024.02.23

    343

    5

    Java 消息队列与异步架构实战
    Java 消息队列与异步架构实战

    本专题系统讲解 Java 在消息队列与异步系统架构中的核心应用,涵盖消息队列基本原理、Kafka 与 RabbitMQ 的使用场景对比、生产者与消费者模型、消息可靠性与顺序性保障、重复消费与幂等处理,以及在高并发系统中的异步解耦设计。通过实战案例,帮助学习者掌握 使用 Java 构建高吞吐、高可靠异步消息系统的完整思路。

    2026.01.28

    203

    20

    RabbitMQ使用教程合集
    RabbitMQ使用教程合集

    RabbitMQ使用教程合集整理 RabbitMQ 基础教程、消息队列开发案例、生产者消费者、交换机与队列实战内容。

    2026.05.20

    80

    15

    RabbitMQ集群部署指南
    RabbitMQ集群部署指南

    RabbitMQ集群部署指南聚合 RabbitMQ 集群部署、高可用架构、镜像队列、故障恢复、监控与运维优化内容。

    2026.05.20

    36

    12

    RabbitMQ Docker实战指南
    RabbitMQ Docker实战指南

    RabbitMQ Docker实战指南提供 RabbitMQ Docker 镜像部署、Docker Compose、Kubernetes Operator 与云原生实践教程。

    2026.05.20

    102

    10

    counta和count的区别
    counta和count的区别

    Count函数用于计算指定范围内数字的个数,而CountA函数用于计算指定范围内非空单元格的个数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

    2023.11.20

    1109

    5

    墨刀AI提示词教学
    墨刀AI提示词教学

    本合集由PHP中文网精心整理,为您提供全面的墨刀AI提示词教学。内容涵盖高质量原型撰写公式与实操窍门,助您轻松掌握AI设计工具。无论是零基础入门还是进阶技巧,都能让您快速上手,大幅提升产品设计与协作效率。

    2026.08.04

    10

    21

    墨刀AI完整入门
    墨刀AI完整入门

    PHP中文网为您倾力打造墨刀AI保姆级入门指南完整版!本合集从零基础讲起,涵盖AI生成原型、提示词优化、图片转原型及多轮对话等核心功能。无论您是新手还是进阶用户,都能轻松掌握产品设计全流程。快来PHP中文网,一键解锁高效设计技巧,让想法即刻成型!

    2026.08.04

    8

    20

    墨刀AI进阶技巧
    墨刀AI进阶技巧

    本合集由PHP中文网精心整理,为您提供墨刀AI核心进阶策略指南。内容涵盖高效提示词写作、原型智能生成与微调、结构化导图制作及行业分析报告输出等实战技巧。助您轻松掌握AI设计工具,大幅提升产品设计与团队协作效率。

    2026.08.04

    10

    14

    热门下载

    更多
    网站特效
    /
    网站源码
    /
    网站素材
    /
    前端模板

    精品课程

    更多
    相关推荐
    /
    热门推荐
    /
    最新课程
    Redis6入门到精通超详细教程
    Redis6入门到精通超详细教程

    共47课时 | 6.7万人学习

    RabbitMQ 入门教程
    RabbitMQ 入门教程

    共0课时 | 92人学习

    RabbitMQ 教程手册
    RabbitMQ 教程手册

    共0课时 | 0人学习