面试题:RocketMQ 如何保证消息不丢失,如何保证消息不被重复消费?

news2024/10/4 8:26:40

文章目录

  • 1、消息整体处理过程
    • Producer发送消息阶段
      • 手段一:提供SYNC的发送消息方式,等待broker处理结果。
      • 手段二:发送消息如果失败或者超时,则重新发送。
      • 手段三:broker提供多master模式,即使某台broker宕机了,保证消息可以投递到另外一台正常的broker上。
    • Broker处理消息阶段
      • 手段四:提供同步刷盘的策略
      • 手段五:提供主从模式,同时主从支持同步双写
    • Consumer消费消息阶段
      • 手段六:consumer默认提供的是At least Once机制
      • 手段七:消费消息重试机制
  • 2、如何保证消息不被重复消费
  • 所以第二个问题来了,怎么保证消息队列消费的幂等性?


1、消息整体处理过程

在这里插入图片描述
这里我们将消息的整体处理阶段分为3个阶段进行分析:

  • Producer发送消息阶段。
  • Broker处理消息阶段。
  • Consumer消费消息阶段。

Producer发送消息阶段

发送消息阶段涉及到Producer到broker的网络通信,因此丢失消息的几率一定会有,那RocketMQ在此阶段用了哪些手段保证消息不丢失了(或者说降低丢失的可能性)。

手段一:提供SYNC的发送消息方式,等待broker处理结果。

RocketMQ提供了3种发送消息方式,分别是:

  • 同步发送:Producer 向 broker 发送消息,阻塞当前线程等待 broker 响应 发送结果。
  • 异步发送:Producer 首先构建一个向 broker 发送消息的任务,把该任务提交给线程池,等执行完该任务时,回调用户自定义的回调函数,执行处理结果。
  • Oneway发送:Oneway 方式只负责发送请求,不等待应答,Producer只负责把请求发出去,而不处理响应结果。

我们在调用producer.send方法时,不指定回调方法,则默认采用同步发送消息的方式,这也是丢失几率最小的一种发送方式。

手段二:发送消息如果失败或者超时,则重新发送。

发送重试源码如下,本质其实就是一个for循环,当发送消息发生异常的时候重新循环发送。默认重试3次,重试次数可以通过producer指定。

手段三:broker提供多master模式,即使某台broker宕机了,保证消息可以投递到另外一台正常的broker上。

如果broker只有一个节点,则broker宕机了,即使producer有重试机制,也没用,因此利用多主模
式,当某台broker宕机了,换一台broker进行投递。

总结
producer消息发送方式虽然有3种,但为了减小丢失消息的可能性尽量采用同步的发送方式,同步等待发送结果,利用同步发送+重试机制+多个master节点,尽可能减小消息丢失的可能性。

Broker处理消息阶段

手段四:提供同步刷盘的策略

public enum FlushDiskType {
SYNC_FLUSH, //同步刷盘
ASYNC_FLUSH//异步刷盘(默认)
}

我们知道,当消息投递到broker之后,会先存到page cache,然后根据broker设置的刷盘策略是否立即刷盘,也就是如果刷盘策略为异步,broker并不会等待消息落盘就会返回producer成功,也就是说当broker所在的服务器突然宕机,则会丢失部分页的消息。

手段五:提供主从模式,同时主从支持同步双写

即使broker设置了同步刷盘,如果主broker磁盘损坏,也是会导致消息丢失。

因此可以给broker指定slave,同时设置master为SYNC_MASTER,然后将slave设置为同步刷盘策略。

此模式下,producer每发送一条消息,都会等消息投递到master和slave都落盘成功了,broker才会当作消息投递成功,保证休息不丢失。

总结
在broker端,消息丢失的可能性主要在于刷盘策略和同步机制。
RocketMQ默认broker的刷盘策略为异步刷盘,如果有主从,同步策略也默认的是异步同步,这样子可以提高broker处理消息的效率,但是会有丢失的可能性。因此可以通过同步刷盘策略+同步slave策略+主从的方式解决丢失消息的可能。

Consumer消费消息阶段

手段六:consumer默认提供的是At least Once机制

从producer投递消息到broker,即使前面这些过程保证了消息正常持久化,但如果consumer消费消息没有消费到也不能理解为消息绝对的可靠。因此RockerMQ默认提供了At least Once机制保证消息可靠消费。

何为At least Once

Consumer先pull 消息到本地,消费完成后,才向服务器返回ack。

通常消费消息的ack机制一般分为两种思路:

  • 先提交后消费;
  • 先消费,消费成功后再提交;

思路一可以解决重复消费的问题但是会丢失消息,因此Rocketmq默认实现的是思路二,由各自consumer业务方保证幂等来解决重复消费问题。

手段七:消费消息重试机制

当消费消息失败了,如果不提供重试消息的能力,则也不能算完全的可靠消费,因此RocketMQ本身提供了重新消费消息的能力。

总结
consumer端要保证消费消息的可靠性,主要通过At least Once+消费重试机制保证。

2、如何保证消息不被重复消费

回答这个问题,首先你别听到重复消息这个事儿,就一无所知吧,你先大概说一说可能会有哪些重复消费的问题。

首先,比如 RabbitMQ、RocketMQ、Kafka,都有可能会出现消息重复消费的问题,正常。因为这问题通常不是 MQ 自己保证的,是由我们开发来保证的。挑一个 Kafka 来举个例子,说说怎么重复消费吧。

Kafka 实际上有个 offset 的概念,就是每个消息写进去,都有一个 offset,代表消息的序号,然后 consumer 消费了数据之后,每隔一段时间(定时定期),会把自己消费过的消息的 offset 提交一下,表示“我已经消费过了,下次我要是重启啥的,你就让我继续从上次消费到的 offset 来继续消费吧”。

但是凡事总有意外,比如我们之前生产经常遇到的,就是你有时候重启系统,看你怎么重启了,如果碰到点着急的,直接 kill 进程了,再重启。这会导致 consumer 有些消息处理了,但是没来得及提交 offset,尴尬了。重启之后,少数消息会再次消费一次。

有这么个场景。数据 1/2/3 依次进入 kafka,kafka 会给这三条数据每条分配一个 offset,代表这条数据的序号,我们就假设分配的 offset 依次是 152/153/154。消费者从 kafka 去消费的时候,也是按照这个顺序去消费。假如当消费者消费了 offset=153 的这条数据,刚准备去提交 offset 到 zookeeper,此时消费者进程被重启了。

那么此时消费过的数据 1/2 的 offset 并没有提交,kafka 也就不知道你已经消费了 offset=153 这条数据。那么重启之后,消费者会找 kafka 说,嘿,哥儿们,你给我接着把上次我消费到的那个地方后面的数据继续给我传递过来。由于之前的 offset 没有提交成功,那么数据 1/2 会再次传过来,如果此时消费者没有去重的话,那么就会导致重复消费。

在这里插入图片描述
如果消费者干的事儿是拿一条数据就往数据库里写一条,会导致说,你可能就把数据 1/2 在数据库里插入了 2 次,那么数据就错啦。

其实重复消费不可怕,可怕的是你没考虑到重复消费之后,怎么保证幂等性。

举个例子吧。假设你有个系统,消费一条消息就往数据库里插入一条数据,要是你一个消息重复两次,你不就插入了两条,这数据不就错了?但是你要是消费到第二次的时候,自己判断一下是否已经消费过了,若是就直接扔了,这样不就保留了一条数据,从而保证了数据的正确性。

一条数据重复出现两次,数据库里就只有一条数据,这就保证了系统的幂等性。

幂等性,通俗点说,就一个数据,或者一个请求,给你重复来多次,你得确保对应的数据是不会改变的,不能出错。

所以第二个问题来了,怎么保证消息队列消费的幂等性?

其实还是得结合业务来思考,我这里给几个思路:

比如你拿个数据要写库,你先根据主键查一下,如果这数据都有了,你就别插入了,update 一下好吧。

比如你是写 Redis,那没问题了,反正每次都是 set,天然幂等性。

比如你不是上面两个场景,那做的稍微复杂一点,你需要让生产者发送每条数据的时候,里面加一个全局唯一的 id,类似订单 id 之类的东西,然后你这里消费到了之后,先根据这个 id 去比如 Redis 里查一下,之前消费过吗?如果没有消费过,你就处理,然后这个 id 写 Redis。如果消费过了,那你就别处理了,保证别重复处理相同的消息即可。

比如基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束了,重复数据插入只会报错,不会导致数据库中出现脏数据。

在这里插入图片描述

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

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

相关文章

【STM32单片机】小恐龙游戏设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用STM32F103C8T6单片机控制器,使用按键、IIC OLED模块等。 主要功能: 系统运行后,OLED液晶显示游戏初始界面,按下K4键开始,K1键跳跃&am…

Twitter优化秘籍:置顶、列表、受众增长

在 Twitter 上,将你的一条推送文置顶到个人数据顶部是提高可见性和吸引关注者的绝佳方式。无论你是个人用户还是企业,此功能都可以让你的重要信息常驻在众人眼前,即使你发布了新的推文。接下来,我们将分享一些优化建议&#xff0c…

机器视觉工程师们,常回家看看

我们在这个社会上扮演着多重角色,有时候我们很难平衡好这些角色之间的关系。 人们常言,积善成德,改变命运。善修者,懂得积累,懂得改变命运的重要性。 我曾年少不知父母之不易。一路依靠,一路成长。 所谓…

【数据增强】

【数据增强】 1 数据增强的情形2 数据增强的方法 1 数据增强的情形 当数据比较小,难以获取新的训练数据时,可以考虑数据增强,如随机裁剪部分,随机左右上下翻转、随机旋转一个角度、随机亮度变化等微小变化,数据的多样…

conan入门(二十七):因profile [env]字段废弃导致的boost/1.81.0 在aarch64-linux-gnu下交叉编译失败

今天在尝试用conan 1.60.0使用aarch64-linux-gnu编译器交叉编译boost/1.81.0时报错了: conan install boost/1.81.0 -pr:h aarch64-linux-gnu.jinja -pr:b default --build boost输出如下: Configuration (profile_host): [settings] archarmv8 arch_b…

iOS——present相关属性以及dismiss多级的方法

push和present 两者的区别 push: push由视图栈控制,每一个视图都入栈,调用之前的视图则需要出栈,可返回任意一层,一般用于同一业务不同界面之间的切换。 push是由UINavigationController管理的视图控制器堆栈,在wind…

CSS3实现上下拉长加载动画效果

上下拉长加载动画效果 <!DOCTYPE html> <html><head><style>.container {display: flex;justify-content: center;align-items: center;width: 150px;height: 150px;margin: 50px auto;}.rectangle {width: 20px;height: 50px;background-color: #02A…

黑马JVM总结(十九)

&#xff08;1&#xff09;GC调优1 通过官网查看查看JVM的参数&#xff1a; 可以使用java命令查看当前环境下的虚拟机参数&#xff1a; 学会使用一些工具如前面学的jmap &#xff0c;jconsole等等工具 &#xff08;2&#xff09;GC调优2 垃圾回收调优只是众多调优中的一个方…

基于微信小程序的电动车智能充电系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言运行环境说明用户的主要功能有&#xff1a;管理员的主要功能有&#xff1a;具体实现截图详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考论文参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌…

THREE.JS 3D模型服务器报404问题(VUE fbx文件在本地能显示 服务器上不显示报404)

问题&#xff1a; 作为新手&#xff0c;新建立的threeJS模型&#xff0c;本地运行模型是可以显示的 &#xff0c;但服务器模型却报404 写法&#xff1a; fbxloader("TR7007Q").then((tree) > { tree.position.set(-1080, -20, 0);this.$refs.draw.scene.add(tree…

编辑.htaccess文件执行任意代码(CVE-2022-25578)

简介 CVE-2022-25578是Taocms v3.0.2中存在的一个安全漏洞&#xff0c;该漏洞允许攻击者通过任意编辑.htaccess文件来执行代码注入攻击。 Taocms是一个完善支持多数据库&#xff08;Sqlite/Mysql&#xff09;的CMS网站内容管理系统&#xff0c;是国内最小且功能完善的基于php…

DM/达梦数据库查询或更新某一列中多个字典码对应内容

准备工作&#xff08;建表、插入数据&#xff09; 1、建立表格&#xff1a;学生-学习科目表student_study 注意&#xff1a;科目kemu列内容是字典码&#xff0c;需要更换成对应内容。 CREATE TABLE "TEST"."STUDENT_STUDY" ( "NAME" VARCHAR(2…

技术分享| anyRTC音视频混流技术解析

一&#xff0c;简介 在视频通讯场景中&#xff0c;比如会议、直播等经常能看到图像合成的场景。图像合成是在指定的一块画面区域&#xff0c;在这个区域内&#xff0c;按画面的位置(坐标)布局&#xff0c;将区域中的每个视频画面的像素混合计算成一个像素&#xff08;RGB&…

深度学习中的激活函数 – 完整概述

1.什么是激活函数? 生物神经网络启发了人工神经网络的发展。然而,人工神经网络甚至不能近似代表大脑的工作方式。在我们知道为什么在人工神经网络中使用激活函数之前,了解生物神经网络中激活函数的相关性仍然很有用。 典型的神经元具有由细胞体、向其他神经元发送信息的轴…

源码:TMS FlexCel Studio for .NET 7.19

TMS FlexCel Studio for .NET 是100% 托管代码 Excel 文件操作引擎以及 Excel 和 PDF 报告生成&#xff0c;适用于 .NET、Xamarin.iOS、Xamarin.Android、Xamarin.Mac、Windows Phone 和 Windows Store 功能概述 使用 FlexCel Studio for .NET 创建可动态快速读写 Excel 文件的…

SQLite 3.43 发布,性能大提升!

前言 SQLite是一种被广泛运用的嵌入式关系型数据库管理系统&#xff0c;最新发布的SQLite 3.43版本带来了一个重要的改进&#xff0c;大幅提升了对JSON数据的处理性能&#xff0c;达到了之前的两倍。 主要更新 添加对 Contentless-Delete FTS5 索引的支持。这是 FTS5 全文搜索…

Go 常用命令介绍

Go 常用命令 文章目录 Go 常用命令一、Go 常用命令1.1 go build1.1.1 指定输出目录1.1.2 常用环境变量设置编译操作系统和 CPU 架构1.1.3 查看支持的操作系统和CPU架构 1.2 go test1.3 go vet1.4 go clean1.5 go fmt1.6 go get1.7 go install1.8 go tool1.9 go generate1.10 go…

iOS——KVC(键值编码)

键值编码&#xff08;KVC&#xff09; KVC&#xff08;Key Value Coding&#xff09;是一种允许以字符串形式间接操作对象属性的方式。 最基本的KVC是由NSKeyValueCoding协议提供支持&#xff0c;最基本的操作属性如下&#xff1a; setValue: 属性值 forKey: 属性名&#xff…

Flink1.14 Source概念入门讲解与源码解析

目录 Flink Source概念 Source Source源码 getBoundedness() createReader(SourceReaderContext readerContext) createEnumerator(SplitEnumeratorContext enumContext) SplitEnumerator restoreEnumerator(SplitEnumeratorContext enumContext, EnumChkT checkpoint) …

使用Selenium进行网页登录和会话管理

随着互联网的快速发展&#xff0c;网页登录和会话管理是许多网站和应用程序的基本功能。通过网页登录&#xff0c;用户可以访问个人账户、购物车订单、历史记录等个性化信息。为了提高用户体验和效率&#xff0c;自动化登录和会话管理成为一个重要的需求。而Selenium作为一种强…