RabbitMQ---消息确认和持久化

(一)消息确认

1.概念

生产者发送消息后,到达消费端会有以下情况:

1.消息处理成功

2.消息处理异常

   如果RabbitMQ把消息发送给消费者后就把消息删除,那么就可能会导致,消息处理异常想要再获取这条消息的时候,造成消息丢失,但如果消息处理成功,不需要这条消息了,就可以进行删除。此时就不会有问题

 所以如何保证消费者成功接收并正确处理?我们RabbitMQ就给我们提供了消息确认机制

 消费者在订阅队列的时候,我们可以指定autoAck参数,根据这个参数,我们可以把确认机制大致分为两种(Spring boot有三种,但是本质也是这两种)

  自动确认:当autoAck为true时,RabbitMQ只要发送了这个消息,就会从内存中删除,不会管消费者是否收到了这些消息,所以自动确认的可靠性不高

  手动确认:当autoAck为false时,RabbitMQ会等待消费者调用Basic.Ack,回复确认信号后,RabbitMQ才会从队列中删除消息,所以手动确认的可靠性是比较高的

当我们设置为手动确认后,队列中的消息就分为了两个部分:

1.等待投递给消费者的消息

2.已经投递给消费者但是没有收到确认信号的消息

也就是Ready和Unacked

如果RabbitMQ一致没有收到消费者的确认信号,并且消息对应的消费者断开连接,RabbitMQ就会安排消息重新入队列,等待投递给下一个消费者

 

 

 

2.Spring Boot的三种策略和代码演示

Springboot给我们提供了三种策略(本质上还是自动确认和手动确认)

1)AcknowledgeMode.NONE

 这种模式下,就是标准的自动确认,消息一旦投递给消费者,不管消费者是否正确处理了消息,RabbitMQ就会自动确认消息,并且从队列中移除,所以消息是很有可能丢失的

 2)AcknowledgeMode.AUTO(默认)

这种模式下,消息成功处理时会自动确认消息,如果消息过程中抛出了异常就不会确认消息

3)AckniwledgeMode.MANUAL

这种模式下,就是标准的手动确认模式,消费者必须在成功处理消息后调用basicAck方法来确认消息,如果消息没有被确认,RabbitMQ就会认为消息没有被成功处理,会重新投递该消息,这种模式会提高消息的可靠性,因为消息不会丢失,而是重新入队

代码演示

配置文件代码

spring:rabbitmq:addresses: amqp://student:student@62.234.46.219:5672/testlistener:simple:acknowledge-mode: NONE

声明交换机和队列代码

@Component
public class Config {@Bean("ackExchange")public Exchange ackExchange(){return ExchangeBuilder.directExchange(Constants.ACK_EXCHANGE).durable(true).build();}@Bean("ackQueue")public Queue ackQueue(){return QueueBuilder.durable(Constants.ACK_QUEUE).build();}@Beanpublic Binding ackBind(@Qualifier("ackExchange") Exchange ackExchange,@Qualifier("ackQueue") Queue queue){return BindingBuilder.bind(queue).to(ackExchange).with("ack").noargs();}
}

生产者代码


@RequestMapping("producer")
@RestController
public class AckProducer {@Autowiredprivate RabbitTemplate rabbitTemplate;@RequestMapping("ack")public String ackPro(){rabbitTemplate.convertAndSend(Constants.ACK_EXCHANGE,"ack","ack test");return "发送成功";}
}

然后我们发送一条消息看现象(此时我们还没写消费者代码,所以没有自动确认) 

消费者代码

@Component
public class AckConsumer {@RabbitListener(queues = Constants.ACK_QUEUE)public void ListenerQueue(Message message, Channel channel){System.out.println("接收到消息: "+ new String(message.getBody())+" TagID: "+message.getMessageProperties().getDeliveryTag());
//        int num=3/0;     //模拟失败System.out.println("处理完成");}
}

此时我们没有出错,我们看能否正确接收消息,已经队列中消息是否存在 

 

我们发现能够准确的接收消息

如果我们此时抛出异常呢? 

 

 

  我们发现抛出了异常后,我们后端并没有接收到消息,但是队列的消息也消失了,所以就会导致我们的消息不见了,这也就是自动确认的弊端 

此时我们把确认策略改成AUTO

我们这里只需要改配置文件即可

截图体现不出来,但是结果是一直在循环,因为我们如果设置确认策略为AUTO,在运行正常时,会自动确认,并且从队列中删除,如果抛出异常,就不会确认消息,造成循环

 

我们再来演示下正确情况 

 

此时我们再来说一下手动确认策略

首先更改配置文件

  其次和我们刚刚演示AUTO和NONE时,代码是没有改动的,但是手动确认,因为要我们手动做一些处理,所以代码也要有一定的改变 

  那我们先来讲一下手动确认方法

手动确认方法:

1.肯定确认

RabbitMQ已经知道该消息并且成功的处理消息,可以丢弃了 

我们来解释下这个方法的参数

deliveryTag:消息的唯一标识,是一个单调递增的64位长整型,每个channel之间是独立的,所以在每个channel上是唯一的,当消费者确认一条消息时,需要用对应的信道上进行确认

multiple:是否批量确认,如果为false,就只确认当前值,如果为true就会确定小于等于的值

2.否定确认

有两个方法

他们两个的区别不大,唯一区别就是是否支持一次性批量拒绝消息

我们来看一些这个requeue这个参数,这个参数表示拒绝后,消息要如何处理,如果为true,RabbitMQ就会把他重新入队,发给下一个消费者,如果为false,RabbitMQ就会把他从队列中删除,不会把他发送给信道消费者

消费者代码

@Component
public class AckConsumer {@RabbitListener(queues = Constants.ACK_QUEUE)public void ListenerQueue(Message message, Channel channel) throws IOException {long Tag=message.getMessageProperties().getDeliveryTag();try {System.out.println("接收到消息: "+ new String(message.getBody())+" TagID: "+Tag);int num=3/0;     //模拟失败channel.basicAck(Tag,false);System.out.println("处理完成");}catch (Exception e){channel.basicNack(Tag,false,true);channel.basicReject(Tag,true);}}
}

出现异常时的现象 

因为我们是设置出现异常重新入队给下一个消费者,所以会循环

如果消息成功接收

 

(二)持久化

  我们刚刚做的消息确认和之前说过七大工作模式中的发送确认都是为了保证消息不丢失,能够正确的接收,但是我们如何保证RabbitMQ服务停掉以后,生产者发送过的消息不丢失(存储在RabbitMQ中的消息)?

1.RabbitMQ的持久化

  RabbitMQ的持久化分为三个部分:交换机持久化,队列持久化和消息持久化

1)交换机持久化

  交换机持久化是通过在声明交换机的时候,将durable参数设置为true实现的,相当于将交换机的属性在服务器中存储,M服务关闭后再次重新,RabbitMQ会自动重新建立交换机,此时就相当于一直存在

上面是我们把交换机设置为持久化的代码

其实我们不手动设置,默认交换机也是持久的,我们来看源码 

 

 

我们发现就算不设置,他默认也是true,在我们创建的时候也是持久化的

2)队列持久化

队列持久化也是在声明队列时调用方法来实现的

如果队列不设置持久化,MQ重启时,队列就会被删掉,与此同时消息是存在队列中的,所以队列上的消息也会小时

所以我们要设置消息持久化,那么一定要设置队列持久化否则没用

这是设置为持久化的代码

这是设置为非持久化的代码

那我们还是来看一眼源码

然后我们点进这个QueueBuilder看看

 

我们发现这个就是给名字赋值用的

再来看看durable中的setDurable

我们发现是设置持久化的,因为是boolean类型,默认都是false所以非持久化就不需要做处理了

3)消息持久化

消息持久化

消息实现持久化就需要把消息的投递模式进行更改

设置了队列和消息的持久化,RabbitMQ服务器重启后,消息才会存在,其他情况下,重新MQ都武器消息都会丢失

消息持久化代码

@RequestMapping("producer")
@RestController
public class AckProducer {@Autowiredprivate RabbitTemplate rabbitTemplate;@RequestMapping("ack")public String ackPro(){String s1="ack test";Message message=new Message(s1.getBytes(StandardCharsets.UTF_8),new MessageProperties());message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);rabbitTemplate.convertAndSend(Constants.ACK_EXCHANGE,"ack",message);return "发送成功";}
}

注意我们这里传的不是一个单独字符串了,而是一个消息 

  RabbitMQ会默认将消息视为持久化的,除非队列被声明为非持久化,或者发送消息时被标记为非持久化 

问题:

 注:如果我们将所有消息都设置为持久化会严重影响RabbitMQ的性能,写入磁盘的速度比写入内存的速度慢很多,所以我们在选择消息持久化的时候,要做一个衡量

如果我们将交换机,队列,消息都设置了持久化后,就能保证百分百不丢失数据了吗?

答案是错误的

1.在持久化消息存入RabbitMQ时,还有一段时间,消息是在缓存上的还没有写入硬盘,因为RabbitMQ并不会为每条消息都同步存盘,因为这样会严重影响性能,所以会有缓存,等待数据一起写入硬盘。如果在这是MQ重启了,消息还没有写入硬盘,那这些消息就会丢失

2.消费者设置自动确认,然后消费者接收消息后宕机,此时消息也会丢失,此时我们只需要设置为手动确认即可

第一个问题的解决方案我们下一篇博客在讲

  

 

 

 

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

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

相关文章

Linux:System V - 共享内存

1.System V共享内存的原理 通过为用户提供系统调用接口,让用户可以申请一块空间,进程A/B也可以通过系统调用接口将创建好的内存通过页表映射进进程的地址空间。完成让不同的两个进程看见同一份资源的目的。如果未来不想继续通信,取消进程和内…

SpringBoot错误码国际化

先看测试效果: 1. 设置中文 2.设置英文 文件结构 1.中文和英文的错误消息配置 package com.ldj.mybatisflex.common;import lombok.Getter;/*** User: ldj* Date: 2025/1/12* Time: 17:50* Description: 异常消息枚举*/ Getter public enum ExceptionEnum {//…

道旅科技借助云消息队列 Kafka 版加速旅游大数据创新发展

作者:寒空、横槊、娜米、公仪 道旅科技:科技驱动,引领全球旅游分销服务 道旅科技 (https://www.didatravel.com/home) 成立于 2012 年,总部位于中国深圳,是一家以科技驱动的全球酒店资源批发商…

Solidity01 Solidity极简入门

一、Solidity 简介 Solidity 是一种用于编写以太坊虚拟机(EVM)智能合约的编程语言。我认为掌握 Solidity 是参与链上项目的必备技能:区块链项目大部分是开源的,如果你能读懂代码,就可以规避很多亏钱项目。 Solidity …

如何使用WPS的JS宏实现Word表格的自动编号

如何使用WPS的JS宏实现Word表格的自动编号&#xff1f;如下图&#xff0c;想要给表格的编号列中添加序号。 使用WPS的JS宏可以实现自动编号&#xff0c;代码如下&#xff1a; n Selection.Tables.Item(1).Rows.Count;for(i 2;i<n;i){Selection.Tables.Item(1).Cell(i,1).…

Python在多个Excel文件中找出缺失数据行数多的文件

本文介绍基于Python语言&#xff0c;针对一个文件夹下大量的Excel表格文件&#xff0c;基于其中每一个文件内、某一列数据的特征&#xff0c;对其加以筛选&#xff0c;并将符合要求与不符合要求的文件分别复制到另外两个新的文件夹中的方法。 首先&#xff0c;我们来明确一下本…

【Linux】应用层自定义协议与序列化

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Linux 目录 一&#xff1a;&#x1f525; 应用层 &#x1f98b; 再谈 "协议"&#x1f98b; 网络版计算器&#x1f98b; 序列化 和 反序列化 二&#xff1a;&#x1f525; 重新理解 read、…

数字化贷款管理:助贷系统软件为贷款中介带来的五大改变

随着金融行业的数字化转型&#xff0c;贷款中介的业务模式也在不断创新。助贷系统作为数字化管理的核心工具&#xff0c;正在为贷款中介带来深刻的变革。本文将从五个方面探讨助贷系统软件如何改变贷款中介行业的管理模式&#xff0c;提升业务效率&#xff0c;降低运营成本。乐…

MIAOYUN信创云原生项目亮相西部“中试”生态对接活动

近日&#xff0c;以“构建‘中试’生态&#xff0c;赋能科技成果转化”为主题的“科创天府智汇蓉城”西部“中试”生态对接活动在成都高新区菁蓉汇隆重开幕。活动分为成果展览、“中试”生态主场以及成果路演洽谈对接三大板块。在成果展览环节&#xff0c;成都元来云志科技有限…

一文简要了解为什么需要RAG、核心原理与应用场景

欢迎来到AI应用探索&#xff0c;这里专注于探索AI应用。 一、为什么需要RAG&#xff0c;它解决了哪些问题 在自然语言处理领域&#xff0c;生成式预训练模型&#xff08;如GPT&#xff09;已经展示了强大的文本生成能力。然而&#xff0c;这些模型有以下局限性&#xff1a; 知…

gametime

gametime 一、查壳 无壳&#xff0c;32位 二、IDA分析 先看看main 妈呀&#xff0c;好多函数&#xff0c;脑子有点乱 先运行下EXE看看有什么突破口没 可以看出是游戏&#xff0c;明显是看你的输入对不对&#xff0c;来通关的&#xff0c;所以有关判定的条件或者函数是解题…

基于机器学习随机森林算法的个人职业预测研究

1.背景调研 随着信息技术的飞速发展&#xff0c;特别是大数据和云计算技术的广泛应用&#xff0c;各行各业都积累了大量的数据。这些数据中蕴含着丰富的信息和模式&#xff0c;为利用机器学习进行职业预测提供了可能。机器学习算法的不断进步&#xff0c;如深度学习、强化学习等…

【Linux】Socket编程-TCP构建自己的C++服务器

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Linux 目录 一&#xff1a;&#x1f525; Socket 编程 TCP &#x1f98b; TCP socket API 详解&#x1f98b; 多线程远程命令执行&#x1f98b; 网络版计算器&#xff08;应用层自定义协议与序列化…

C++(二十一)

前言&#xff1a; 本文承接上文&#xff0c;将详细讲解指针概念。 一&#xff0c;通过指针了解变量的数值。 在将变量地址存入指针后&#xff0c;从指针反推也可以知道原变量的值&#xff0c;若想进行反退&#xff0c;就需要使用间接引用运算符&#xff1a;*。 语法&#x…

Windows图形界面(GUI)-QT-C/C++ - Qt List Widget详解与应用

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​链接点击跳转博客主页 目录 QListWidget概述 使用场景 常见样式 QListWidget属性设置 显示方式 (Display) 交互行为 (Interaction) 高级功能 (Advanced) QListWidget常见操作 内容处理 增加项目 删除项目…

一文了解如何使用 DBeaver 管理 DolphinDB

在日常的数据开发、分析和数据库运维中&#xff0c;一款优秀的 IDE 能够极大地提升工作效率。DBEaver 是一款由 Java 编写的一站式跨平台连接器&#xff0c;其社区版本已能支持连接近百种数据库&#xff0c;受到广大开发者的喜爱。近期。DolphinDB 与 DBeaver 团队共同努力&…

CSRF攻击XSS攻击

概述 ​在 HTML 中&#xff0c;<a>, <form>, <img>, <script>, <iframe>, <link> 等标签以及 Ajax 都可以指向一个资源地址&#xff0c;而所谓的跨域请求就是指&#xff1a;当前发起请求的域与该请求指向的资源所在的域不一样。这里的域指…

【机器学习实战入门】使用 Pandas 和 OpenCV 进行颜色检测

Python 颜色检测项目 今天的项目将非常有趣和令人兴奋。我们将与颜色打交道&#xff0c;并在项目过程中学习许多概念。颜色检测对于识别物体来说是必要的&#xff0c;它也被用作各种图像编辑和绘图应用的工具。 什么是颜色检测&#xff1f; 颜色检测是检测任何颜色名称的过程…

vue2 web 多标签输入框 elinput是否当前焦点

又来分享一点点工作积累及解决方案 产品中需要用户输入一些文字后按下回车键生成标签来显示在页面上&#xff0c;经过尝试与改造完成如下&#xff1a; <template><div class"tags-view" click"beginInput"><el-tag :key"index" …

SSE 实践:用 Vue 和 Spring Boot 实现实时数据传输

前言 大家好&#xff0c;我是雪荷。最近我在灵犀 BI 项目中引入了 SSE 技术&#xff0c;以保证图表的实时渲染&#xff0c;当图表渲染完毕服务端推送消息至浏览器端触发重新渲染。 什么是 SSE&#xff1f; SSE 全称为 Server-Send Events 意思是服务端推送事件。 SSE 相比于 …