Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >引入RabbitMQ后,如何保证全链路数据100%不丢失?

引入RabbitMQ后,如何保证全链路数据100%不丢失?

作者头像
码农架构
发布于 2021-12-16 04:09:10
发布于 2021-12-16 04:09:10
46800
代码可运行
举报
文章被收录于专栏:码农架构码农架构
运行总次数:0
代码可运行
导读:这是一个常见的面试题:引入RabbitMQ后,你如何保证全链路数据100%不丢失?整理本篇文章希望对从事相关工作的同学能够有所帮助或者启发。

我们都知道,消息从生产端到消费端消费要经过3个步骤:

  • 生产端发送消息到RabbitMQ;
  • RabbitMQ发送消息到消费端;
  • 消费端消费这条消息;

这3个步骤中的每一步都有可能导致消息丢失,消息丢失不可怕,可怕的是丢失了我们还不知道,所以要有一些措施来保证系统的可靠性。这里的可靠并不是一定就100%不丢失了,磁盘损坏,机房爆炸等等都能导致数据丢失,当然这种都是极小概率发生,能做到99.999999%消息不丢失,就是可靠的了。下面来具体分析一下问题以及解决方案。

生产端可靠性投递

生产端可靠性投递,即生产端要确保将消息正确投递到RabbitMQ中。生产端投递的消息丢失的原因有很多,比如消息在网络传输的过程中发生网络故障消息丢失,或者消息投递到RabbitMQ时RabbitMQ挂了,那消息也可能丢失,而我们根本不知道发生了什么。针对以上情况,RabbitMQ本身提供了一些机制。

事务消息机制

事务消息机制由于会严重降低性能,所以一般不采用这种方法,我就不介绍了,而采用另一种轻量级的解决方案——confirm消息确认机制。

confirm消息确认机制

什么是confirm消息确认机制?顾名思义,就是生产端投递的消息一旦投递到RabbitMQ后,RabbitMQ就会发送一个确认消息给生产端,让生产端知道我已经收到消息了,否则这条消息就可能已经丢失了,需要生产端重新发送消息了。

通过下面这句代码来开启确认模式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
channel.confirmSelect();// 开启发送方确认模式

然后异步监听确认和未确认的消息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
channel.addConfirmListener(new ConfirmListener() {
    //消息正确到达broker
    @Override
    public void handleAck(long deliveryTag, boolean multiple) throws IOException {
        System.out.println("已收到消息");
        //做一些其他处理
    }

    //RabbitMQ因为自身内部错误导致消息丢失,就会发送一条nack消息
    @Override
    public void handleNack(long deliveryTag, boolean multiple) throws IOException {
        System.out.println("未确认消息,标识:" + deliveryTag);
        //做一些其他处理,比如消息重发等
    }
});

这样就可以让生产端感知到消息是否投递到RabbitMQ中了,当然这样还不够,稍后我会说一下极端情况。

消息持久化

那消息持久化呢?我们知道,RabbitMQ收到消息后将这个消息暂时存在了内存中,那这就会有个问题,如果RabbitMQ挂了,那重启后数据就丢失了,所以相关的数据应该持久化到硬盘中,这样就算RabbitMQ重启后也可以到硬盘中取数据恢复。那如何持久化呢?

message消息到达RabbitMQ后先是到exchange交换机中,然后路由给queue队列,最后发送给消费端。

所有需要给exchange、queue和message都进行持久化:

exchange持久化:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//第三个参数true表示这个exchange持久化
channel.exchangeDeclare(EXCHANGE_NAME, "direct", true);

queue持久化:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//第二个参数true表示这个queue持久化
channel.queueDeclare(QUEUE_NAME, true, false, false, null);

message持久化:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//第三个参数MessageProperties.PERSISTENT_TEXT_PLAIN表示这条消息持久化
channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes(StandardCharsets.UTF_8));

这样,如果RabbitMQ收到消息后挂了,重启后会自行恢复消息。

到此,RabbitMQ提供的几种机制都介绍完了,但这样还不足以保证消息可靠性投递RabbitMQ中,上面我也提到了会有极端情况,比如RabbitMQ收到消息还没来得及将消息持久化到硬盘时,RabbitMQ挂了,这样消息还是丢失了,或者RabbitMQ在发送确认消息给生产端的过程中,由于网络故障而导致生产端没有收到确认消息,这样生产端就不知道RabbitMQ到底有没有收到消息,就不好做接下来的处理。

所以除了RabbitMQ提供的一些机制外,我们自己也要做一些消息补偿机制,以应对一些极端情况。接下来我就介绍其中的一种解决方案——消息入库。

消息入库

消息入库,顾名思义就是将要发送的消息保存到数据库中。

首先发送消息前先将消息保存到数据库中,有一个状态字段status=0,表示生产端将消息发送给了RabbitMQ但还没收到确认;在生产端收到确认后将status设为1,表示RabbitMQ已收到消息。这里有可能会出现上面说的两种情况,所以生产端这边开一个定时器,定时检索消息表,将status=0并且超过固定时间后(可能消息刚发出去还没来得及确认这边定时器刚好检索到这条status=0的消息,所以给个时间)还没收到确认的消息取出重发(第二种情况下这里会造成消息重复,消费者端要做幂等性),可能重发还会失败,所以可以做一个最大重发次数,超过就做另外的处理。

这样消息就可以可靠性投递到RabbitMQ中了,而生产端也可以感知到了。

消费端消息不丢失

既然已经可以让生产端100%可靠性投递到RabbitMQ了,那接下来就改看看消费端的了,如何让消费端不丢失消息。

默认情况下,以下3种情况会导致消息丢失:

  • 在RabbitMQ将消息发出后,消费端还没接收到消息之前,发生网络故障,消费端与RabbitMQ断开连接,此时消息会丢失;
  • 在RabbitMQ将消息发出后,消费端还没接收到消息之前,消费端挂了,此时消息会丢失;
  • 消费端正确接收到消息,但在处理消息的过程中发生异常或宕机了,消息也会丢失。

其实,上述3中情况导致消息丢失归根结底是因为RabbitMQ的自动ack机制,即默认RabbitMQ在消息发出后就立即将这条消息删除,而不管消费端是否接收到,是否处理完,导致消费端消息丢失时RabbitMQ自己又没有这条消息了。

所以就需要将自动ack机制改为手动ack机制。

消费端手动确认消息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
    try {
        //接收到消息,做处理
        //手动确认
        channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
    } catch (Exception e) {
        //出错处理,这里可以让消息重回队列重新发送或直接丢弃消息
    }
};
//第二个参数autoAck设为false表示关闭自动确认机制,需手动确认
channel.basicConsume(QUEUE_NAME, false, deliverCallback, consumerTag -> {});

这样,当autoAck参数置为false,对于RabbitMQ服务端而言,队列中的消息分成了两个部分:一部分是等待投递给消费端的消息;一部分是已经投递给消费端,但是还没有收到消费端确认信号的消息。如果RabbitMQ一直没有收到消费端的确认信号,并且消费此消息的消费端已经断开连接或宕机(RabbitMQ会自己感知到),则RabbitMQ会安排该消息重新进入队列(放在队列头部),等待投递给下一个消费者,当然也有能还是原来的那个消费端,当然消费端也需要确保幂等性。

好了,到此从生产端到RabbitMQ再到消费端的全链路,就可以保证数据的不丢失。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-12-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农架构 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
爆肝3万字,为你吃透RabbitMQ,最详细的RabbitMQ讲解(VIP典藏版)
早在之前就了解到了消息中间件,但是一直没有系统的学习,最近花了一段时间系统学习了当下最为主流的 RabbitMQ 消息队列,学习过程中也随时记录,刚开始学习的时候懵懵懂懂,做的笔记都比较杂乱,系统学习完后我将笔记内容不断反复修改,对章节进行设计调整,最终整合出了以下好理解、案例多、超详细的 RabbitMQ 学习笔记,希望能帮到大家~
SmallRoll小卷
2023/03/05
12.5K0
爆肝3万字,为你吃透RabbitMQ,最详细的RabbitMQ讲解(VIP典藏版)
RabbitMQ如何解决各种情况下丢数据的问题
如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。
java架构师
2019/03/06
1.8K0
RabbitMQ消息的可靠性投递
在项目中使用RabbitMQ时,我们可能会遇到这样的问题:如一个订单系统当用户付款成功时我们往消息中间件添加一条记录期望消息消费者修改订单状态,但是最终实际订单状态并没有被修改成功。遇到这种问题我们排查的思路如下:
诺浅
2020/08/21
1.2K0
RabbitMQ如何保证消息的可靠性
如何做到以上几点?RabbitMQ为了适应各个场景的使用,以上的功能需要开发者按照定义自行设置实现。
王二蛋
2024/02/07
2440
如何保证消息的可靠性传输?如何处理消息丢失的问题?
这个是肯定的,用 MQ 有个基本原则,就是数据不能多一条,也不能少一条,不能多,就是前面说的重复消费和幂等性问题。不能少,就是说这数据别搞丢了。那这个问题你必须得考虑一下。
MickyInvQ
2022/05/06
1K0
如何保证消息的可靠性传输?如何处理消息丢失的问题?
029. RabbitMQ 消息可靠性和插件机制
1. 消息可靠性 ---- RabbitMQ 的消息可靠性,一般是业务系统接入消息中间件时首要考虑的问题,一般通过三个方面保障: 发送可靠性:确保消息成功发送到 Broker。 存储可靠性:Broker 对消息持久化,确保消息不会丢失。 消费可靠性:确保消息成功被消费。 1. 发送可靠性 一般消息发送可靠性分为 3 个层级: At most once:最多一次,消息可能会丢失,但绝不会重复传输。 At least once:最少一次,消息绝不会丢失,但可能会重复传输。 Exactly once:
山海散人
2021/03/03
4090
029. RabbitMQ 消息可靠性和插件机制
【Rabbitmq篇】RabbitMQ⾼级特性----持久性,发送⽅确认,重试机制
RabbitMQ 的持久化是确保消息在服务器重启或意外故障后不会丢失的重要特性。它主要涉及到交换机(Exchange)、队列(Queue)和消息(Message)三个层面的持久化。
用户11369558
2024/11/25
1.5K0
【Rabbitmq篇】RabbitMQ⾼级特性----持久性,发送⽅确认,重试机制
精选RabbitMQ面试题[45题]
RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ服务器是用Erlang语言编写的,而群集和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。
一行Java
2022/04/07
1.6K0
精选RabbitMQ面试题[45题]
全网最全RabbitMQ总结,别再说你不会RabbitMQ
当初我学RabbitMQ的时候,第一时间就上GitHub找相应的教程,但是令我很失望的是没有找到,Spring,Mybatis之类的教程很多,而RabbitMQ的教程几乎找不到,看的最多的就是朱小厮大佬的博客。后来想着索性自己总结一下吧,有不恰当的地方欢迎小伙伴指出。
Java识堂
2020/01/17
2.7K0
全网最全RabbitMQ总结,别再说你不会RabbitMQ
【云原生进阶之PaaS中间件】第四章RabbitMQ-4.3-如何保证消息的可靠性投递与消费
根据RabbitMQ的工作模式,一条消息从生产者发出,到消费者消费,需要经历以下4个步骤:
江中散人_Jun
2024/02/21
2450
【云原生进阶之PaaS中间件】第四章RabbitMQ-4.3-如何保证消息的可靠性投递与消费
RabbitMQ学习之消息可靠性及特性
转载自 https://blog.csdn.net/zhu_tianwei/article/details/53971296
allsmallpig
2021/02/25
5960
RabbitMQ消息丢失的场景,如何保证消息不丢失?(详细讲解,一文看懂)
2007 年发布,是一个在 AMQP(高级消息队列协议)基础上完成的,可复用的企业消息系统,是当前最主流的消息中间件之一。
终有救赎
2023/10/16
6.1K0
RabbitMQ消息丢失的场景,如何保证消息不丢失?(详细讲解,一文看懂)
如何避免RabbitMQ消息丢失?
面试经常会被问到“怎么避免RabbitMQ消息不丢失”。首先需要明确的是,RabbitMQ在默认情况并不会保证消息的不丢失。不过RabbitMQ提供了一些机制,可以有效的避免消息在传输和处理过程中不丢失。
闻说社
2024/08/12
2740
如何避免RabbitMQ消息丢失?
《RabbitMQ》 | 消息丢失也就这么回事
大家好,我是小菜。一个希望能够成为 吹着牛X谈架构 的男人!如果你也想成为我想成为的人,不然点个关注做个伴,让小菜不再孤单!
蔡不菜丶
2021/11/02
2.5K0
RabbitMQ消息的可靠性投递
在RabbitMQ工作的过程中,每个环节消息都可能传递失败,那么RabbitMQ是如何监听消息是否成功投递的呢?
会洗碗的CV工程师
2024/05/01
3651
RabbitMQ消息的可靠性投递
四种途径提高RabbitMQ传输数据的可靠性(二)
针对问题(1),我们可以通过生产者的确认消息机制来解决,主要分为两种:第一是事务机制、第二是发送方确认机制
java架构师
2019/03/08
5660
四种途径提高RabbitMQ传输数据的可靠性(二)
RabbitMQ---消息队列---上半部分
MQ(message queue),从字面意思上看,本质是个队列,FIFO 先入先出,只不过队列中存放的内容是message 而已,还是一种跨进程的通信机制,用于上下游传递消息。在互联网架构中,MQ 是一种非常常见的上下游“逻辑解耦+物理解耦”的消息通信服务。使用了 MQ 之后,消息发送上游只需要依赖 MQ,不用依赖其他服务。
大忽悠爱学习
2021/12/07
1.1K0
RabbitMQ---消息队列---上半部分
【35期】如何保证消息的可靠性传输?
这个是肯定的,用 MQ 有个基本原则,就是数据不能多一条,也不能少一条,不能多,就是前面说的重复消费和幂等性问题。不能少,就是说这数据别搞丢了。那这个问题你必须得考虑一下。
架构狂人
2023/09/21
2360
【35期】如何保证消息的可靠性传输?
RabbitMQ全面总结
是指应用程序对应用程序的一种高效可靠的消息传递的通信方法,通过提供消息传递和消息排序模型,他可以在分布式环境下扩展进程间的通信,典型的生产者和消费者的代表
小土豆Yuki
2021/04/08
5730
springboot + rabbitmq 用了消息确认机制,感觉掉坑里了
最近部门号召大伙多组织一些技术分享会,说是要活跃公司的技术氛围,但早就看穿一切的我知道,这 T M 就是为了刷KPI。不过,话说回来这的确是件好事,与其开那些没味的扯皮会,多做技术交流还是很有助于个人成长的。
程序员小富
2020/07/01
7800
springboot + rabbitmq 用了消息确认机制,感觉掉坑里了
推荐阅读
相关推荐
爆肝3万字,为你吃透RabbitMQ,最详细的RabbitMQ讲解(VIP典藏版)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验