当前位置:

首页 > 编程开发 > SpringBoot怎么整合RabbitMQ实现延迟队列

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

如何保证消息不丢失rabbitmq消息投递路径生产者->交换机->队列->消费者总的来说分为三个阶段。1.生产者保证消息投递可靠性。2.mq内部消息不丢失。3.消费者消费成功。什么是消息投递可靠性简单点说就是消息百分百发送到消息队列中。我们可以开启confirmCallback生产者投递消息后,mq会给生产者一个ack.根据ack,生产者就可以确认这条消息是否发送到mq.开启confirmCallback修改配置文件#NONE:禁用发布确认模式,是默认值,CORRELATED:发布消息成功到交换器后会触发

本文目录

      如何保证消息不丢失

      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 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();  
          }  
        
        
      }

      消费者

      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=测试订单延迟

      本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
      作者最新文章
      编程开发
      相关文章 更多
      RabbitMQ Fanout Exchange 多消费者正确实现指南
      RabbitMQ Fanout Exchange 多消费者正确实现指南

      在Go中使用RabbitMQFanoutExchange时,多个消费者需同时接收消息。正确做法是声明fanout类型交换器,并为每个消费者创建独立队列并绑定到该交换器,确保交换器类型正确、队列名唯一,RabbitMQ会将消息广播至所有绑定队列,实现一对多分发。

      Centos 6.4 安装erlang&rabbitmq的方法
      Centos 6.4 安装erlang&rabbitmq的方法

      在开始搭建消息队列服务之前,得先搞定两个核心组件:Erlang 和 RabbitMQ。Erlang 是 RabbitMQ 的运行时环境,所以顺序不能乱——先装 Erlang,再装 RabbitMQ。下面把整个过程拆开来讲,每一步都配了具体的命令,照着走基本不会出错。 1. 安装 Erlang 1.1

      Symfony 将RabbitMQ消息转为数组方法
      Symfony 将RabbitMQ消息转为数组方法

      答案:将Symfony中RabbitMQ消息转为数组需根据消息体格式选择反序列化方式,常见为JSON或PHP序列化;若为JSON,使用json_decode($messageBody,true)转换并校验错误;若为PHP序列化,使用unserialize()但需注意安全风险;其他格式则用对应解析器;若消息封装在对象中,需先提取消息体。

      Python RabbitMQ教程:消息队列入门指南
      Python RabbitMQ教程:消息队列入门指南

      Python操作RabbitMQ的关键在于理解其消息队列流程和工作机制,安装pika库后即可通过简单步骤实现消息发送与接收。首先需安装pika:pipinstallpika,并确保RabbitMQ服务运行;其次,发送消息时需建立连接、打开通道、声明队列并发送消息;接着,接收消息时需监听队列、定义回调函数处理消息,并注意确认机制与连接关闭;此外,实际应用中应关注异常处理、消息持久化、多消费者支持及死信队列等特性以提升系统健壮性。掌握这些步骤和注意事项后,Python结合RabbitMQ的基础操作便能快速上手

      PHP实战:RabbitMQ消息队列详解
      PHP实战:RabbitMQ消息队列详解

      PHP结合RabbitMQ构建异步处理系统需安装RabbitMQ和AMQP扩展1,通过AMQP类连接服务器2,声明交换机和队列并绑定路由键3,使用publish方法发布消息4,利用consume消费消息并调用ack确认5,为避免消息丢失应启用持久化、发布者确认或事务机制6,可通过RabbitMQManagementPlugin、命令行工具或Prometheus+Grafana监控性能7,配置死信队列处理无法消费的消息8,保证消息顺序可通过单消费者、单一队列或分组排序等策略实现9。

      springBoot项目常用目录有哪些
      springBoot项目常用目录有哪些

      springBoot项目常用目录springBoot项目的目录结构及名规范介绍基于SpringBoot开发时的目录结构及命名规范,通过介绍能够为你解决,在实际项目中如何规划目录结构?如何命名目录更规范?各个目录分别代表什么含义?等三个问题。目录说明servicex//项目名|-admin-ui//管理服务前端代码(一般将UI和SERVICE放到一个工程中,便于管理)|-servicex-auth//模块1|-servicex-common//模块2|-servicex-gateway//模块3|-serv

      学习如何安装、配置、部署和使用宝塔面板中的RabbitMQ可视化工具
      学习如何安装、配置、部署和使用宝塔面板中的RabbitMQ可视化工具

      RabbitMQ是一款开源的消息代理和队列服务器,旨在通过通用协议实现不同应用之间的数据共享,实现跨平台和跨语言的通信。该系统采用Erlang语言编写,并基于AMQP协议。其最显著的特点之一是在提供高度解耦的服务之间无需确保提供方的存在。接下来介绍关于如何在宝塔面板上可视化安装、配置、部署、使用RabbitMQ。一、RabbitMQ的特点1.可靠性RabbitMQ通过诸如持久化、传输确认和发布确认等机制来确保消息的可靠性。2.灵活的路由消息在进入队列之前通过交换器进行路由,可按照特定规则进行灵活分发。3.

      PHP开发中如何使用RabbitMQ实现消息传递
      PHP开发中如何使用RabbitMQ实现消息传递

      RabbitMQ是一种消息队列软件,可用于在应用程序之间进行消息传递。在PHP开发中,使用RabbitMQ可以实现异步处理任务、实现分布式系统等。本篇文章将介绍如何在PHP开发中使用RabbitMQ实现消息传递。一、安装RabbitMQ服务RabbitMQ是开源软件,可从其官网(https://www.rabbitmq.com/download.html)下

      PHP与RabbitMQ结合实现消息队列处理和分发的方法
      PHP与RabbitMQ结合实现消息队列处理和分发的方法

      随着互联网业务的不断发展,系统间的数据交换变得越来越频繁。在传统的请求响应式架构中,数据传递的方式往往是同步的,即等待请求响应结果后才能进行下一步操作,当数据量较大或者请求响应时间较长时,这种方式往往会导致系统性能下降。此时,消息队列就成为一种比较理想的解决方案,可以实现异步、解耦、扩展性好等优点。RabbitMQ作为一款消息队列中间件,提供了丰富的API,

      比较RabbitMQ和Kafka:如何选择高效的消息中间件
      比较RabbitMQ和Kafka:如何选择高效的消息中间件

      RabbitMQ和Kafka的比较:选择高效的消息中间件简介消息中间件是一种软件,它允许应用程序通过异步消息传递进行通信。消息中间件可以用于各种目的,包括:解耦应用程序:消息中间件可以将应用程序解耦,使它们能够独立于彼此开发和部署。提高可伸缩性:消息中间件可以帮助应用程序提高可伸缩性,使它们能够处理更多的并发请求。提高可靠性:消息中间件可以帮助应用程序提高可

      查看更多
      精品专题 更多
      装机必备
      装机必备

      正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

      Windows
      Windows

      正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

      PDF教程
      PDF教程

      正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

      Mac软件 更多
      讯飞智作 网页版
      讯飞智作 网页版
      Windows/ macOS/ Linux

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      火山引擎 网页版
      火山引擎 网页版
      Windows/ macOS/ Linux

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      Aperty macOS版
      Aperty macOS版
      macOS

      Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

      WINDOWS 更多
      讯飞智作 网页版
      讯飞智作 网页版
      Windows/ macOS/ Linux

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      火山引擎 网页版
      火山引擎 网页版
      Windows/ macOS/ Linux

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      PixTV 网页版
      PixTV 网页版
      Windows

      PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。