【消息中间件】RocketMQ如何保证消息的可靠性?

news2024/11/23 23:47:46

文章目录

  • 前言
  • 一 、发送端消息可靠性
    • 1. 同步发送
    • 2. 异步发送
    • 3. 单向发送
    • 4. 发送重试策略
  • 二、存储端消息可靠性
    • 1. 存储可靠性挑战
    • 2. 同步刷盘
    • 3. 异步刷盘(默认)
    • 4. 过期文件删除
  • 三、消费端消息可靠性
    • 1. 消费重试
    • 2. 死信队列
    • 3. 消息回溯
  • 四、总结
  • 参考与感谢

前言

消息的发送方式有哪几种?存储消息的可靠性面临哪些挑战?消费消息的确认机制是怎样的?本文通过分析消息流转的整个过程,从消息发送、消息存储和消息消费三个阶段介绍RocketMQ是如何保证消息的可靠性的。

分布式系统中一个重要的前提假设是所有的网络传输都是不可靠的,在网络传输不可靠的情况下,保证消息的可靠传输,除了进行重试投递别无他法。常用的绝大多数消息队列RocketMQ、RabbitMQ等在消息传输上都只能保证至少传输成功一次,也即(At least once),而不能保证只传输成功一次(Exactly once)。由于分布式系统网络的不可靠,可能就会出现消息丢失的现象,那么RocketMQ是如何最大限度的保证消息不丢失的呢?那就需要从消息的产生到最终消费的整个过程来分析,消息完整链路可以划分为以下三个阶段:

  • 生产阶段:消息在 Producer 发送端创建出来,经过网络传输发送到 Broker 存储端。
  • 存储阶段:消息在 Broker 端存储,如果是主备或者多副本,消息会在这个阶段被复制到其他的节点或者副本上。
  • 消费阶段:Consumer 消费端从 Broker存储端拉取消息,经过网络传输发送到 Consumer 消费端上,并通过重试来最大限度的保证消息的消费。

一 、发送端消息可靠性

发送端Producer发送消息Broker端的核心逻辑如下图所示:

在这里插入图片描述

消息发送一般有以下几种方式:同步发送、异步发送以及单向发送,业务具体选择哪种方式进行消息发送,需要根据情况进行判断,下面具体介绍不同的发送方式实现的消息可靠性保证。

1. 同步发送

同步发送是指发送端在发送消息时,阻塞线程进行等待,直到服务器返回发送的结果。发送端如果需要保证消息的可靠性,防止消息发送失败,可以采用同步阻塞式的发送,然后同步检查Brocker返回的状态来判断消息是否持久化成功。如果发送超时或者失败,则会默认重试2次,RocketMQ选择至少传输成功一次的消息模型,但是有可能发生重复投递,因为网络传输是不可靠的,具体的重试策略可以参照第四小节。

2. 异步发送

异步发送是指发送端在发送消息时,传入回调接口实现类,调用该发送接口后不会阻塞,发送方法会立即返回,回调任务会在另一个线程中执行,消息发送结果会回传给相应的回调函数。具体的业务实现可以根据发送的结果信息来判断是否需要重试来保证消息的可靠性。

3. 单向发送

单向发送是指发送端发送完成之后,调用该发送接口后立刻返回,并不返回发送的结果,业务方无法根据发送的状态来判断消息是否发送成功,单向发送相对前两种发送方式来说是一种不可靠的消息发送方式,因此要保证消息发送的可靠性,不推荐采用这种方式来发送消息。

4. 发送重试策略

RocketMQ架构模型中会有多个Borker为某个topic提供服务,一个topic下的消息分散存储在多个Broker存储端,它们是多对多关系。Broker会将其提供存储服务的topic的元数据信息上报到NameServer,对等NameServer节点组成的高可用服务会维护topic与Broker之间的映射关系,多对多的映射关系为消息可以重试发送到多个Broker端提供了前提与基础。

当发送端需要发送消息时,如果发送端中缓存了topic的路由信息,并包含了消息队列,则直接返回该路由信息,如果没有缓存或没有消息队列,则向NameServer查询该topic的路由信息,查询到路由消息之后,采用指定的队列选择策略选择相应的queue发送消息,默认是采用轮询策略,发送成功则返回, 收到异常则根据相应的策略进行重试,可以根据发送端感知到的Broker的时延、上次发送失败的Broker信息和发送端配置的是否重试不同Broker的参数以及发送端设置的最大超时时间等等策略来灵活地实现不同等级的消息发送可靠性保证。重试策略可以有效的保证消息发送成功的概率,最终提高消息发送的可靠性。

二、存储端消息可靠性

RocketMQ的消息存储结构如下图所示:

在这里插入图片描述

  • 消息队列存储的最小单位是消息Message。
  • 同一个Topic下的消息映射成多个逻辑队列。
  • 不同Topic的消息按照到达broker的先后顺序以Append的方式添加至CommitLog,顺序写,随机读。

目前RocketMQ存储模型使用本地磁盘进行存储,数据写入为producer -> direct memory -> pagecache -> 磁盘,数据读取如果pagecache有数据则直接从pagecache读,否则需要先从磁盘加载到pagecache中。Broker存储节点的文件存储模式如下图所示:

在这里插入图片描述

Broker端CommitLog采用顺序写,可以大大提高写入效率,同时采用不同的刷盘模式提供不同的数据可靠性保证,此外采用了ConsumeQueue中间结构来存储偏移量信息,实现消息的分发。由于ConsumeQueue结构固定且大小有限,在实际情况中,大部分的ConsumeQueue 能够被全部读入内存,可以达到内存读取的速度。此外为了保证CommitLog和ConsumeQueue的一致性, CommitLog里存储了Consume Queues 、Message Key、Tag等所有信息,即使ConsumeQueue丢失,也可以通过 commitLog完全恢复出来,这样只要保证commitLog数据的可靠性,就可以保证Consume Queue的可靠性。

RocketMQ存储端采用本地磁盘进行CommitLog消息数据的存储,不可避免的就会带来存储可靠性的挑战,如何保证消息不丢失,RocketMQ消息服务一直在不断提高数据的可靠性。

1. 存储可靠性挑战

RocketMQ存储端也即Broker端在存储消息的时候会面临以下的存储可靠性挑战:

  • Broker正常关闭
  • Broker异常Crash
  • OS Crash
  • 机器掉电,但是能立即恢复供电情况
  • 机器无法开机(可能是cpu、主板、内存等关键设备损坏)
  • 磁盘设备损坏

1正常关闭,Broker 可以正常启动并恢复所有数据。2、3、4同步刷盘可以保证数据不丢失,异步刷盘可能导致少量数据丢失。5、6属于单点故障,且无法恢复。解决单点故障可以采用增加Slave节点,主从异步复制仍然可能有极少量数据丢失,同步复制可以完全避免单点问题。

这里一般来说就需要在性能和可靠性之间做出取舍,对于RocketMQ来说,Broker的可靠性主要由两个方面保障:

  • 单机的刷盘机制
  • 主从之间的数据复制

如果设置为每条消息都强制刷盘、主从复制,那么性能无疑会降低;如果不这样设置,就会有一定的可能性丢失消息。RocketMQ一般都是先把消息写到PageCache中,然后再持久化到磁盘上,数据从pagecache刷新到磁盘有两种方式,同步和异步。整体的消息写入和读取如下图所示:

在这里插入图片描述

针对broker端单机存储可靠性,主要依赖单机的刷盘策略,主从之间的副本复制可以参考下一章节的主从模式。

2. 同步刷盘

消息写入内存的 PageCache后,立刻通知刷盘线程刷盘,然后等待刷盘完成,刷盘线程执行完成后唤醒等待的线程,返回消息写成功的状态。这种方式可以保证数据绝对安全,但是吞吐量不大。

3. 异步刷盘(默认)

消息写入到内存的 PageCache中,就立刻给客户端返回写操作成功,当 PageCache中的消息积累到一定的量时,触发一次写操作,或者定时等策略将 PageCache中的消息写入到磁盘中。这种方式吞吐量大,性能高,但是 PageCache中的数据可能丢失,不能保证数据绝对的安全。

实际应用中要结合业务场景,合理设置刷盘方式,尤其是同步刷盘的方式,由于频繁的触发磁盘写动作,会明显降低性能。

4. 过期文件删除

由于RocketMQ操作CommitLog、ConsumeQueue文件是基于文件内存映射机制,并且在启动的时候会将所有的文件加载,为了避免内存与磁盘的浪费、能够让磁盘能够循环利用、避免因为磁盘不足导致消息无法写入等引入了文件过期删除机制。最终使得磁盘水位保持在一定水平,最终保证新写入消息的可靠存储。

三、消费端消息可靠性

RockerMQ默认提供了至少消费一次的消费语义来保证消息的可靠消费。

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

  1. 先提交后消费
  2. 先消费,消费成功后再提交

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

消费端Consumer消费消息核心逻辑如下图所示:

在这里插入图片描述

1. 消费重试

消费者从RocketMQ拉取到消息之后,需要返回消费成功来表示业务方正常消费完成。因此只有返回CONSUME_SUCCESS才算消费完成,如果返回CONSUME_LATER则会按照不同的messageDelayLevel时间进行再次消费,时间分级从秒到小时,最长时间为2个小时后再次进行消费重试,如果消费满16次之后还是未能消费成功,则不再重试,会将消息发送到死信队列,从而保证消息存储的可靠性。

2. 死信队列

未能成功消费的消息,消息队列并不会立刻将消息丢弃,而是将消息发送到死信队列,其名称是在原队列名称前加%DLQ%,如果消息最终进入了死信队列,则可以通过RocketMQ提供的相关接口从死信队列获取到相应的消息,保证了消息消费的可靠性。

3. 消息回溯

回溯消费是指Consumer已经消费成功的消息,或者之前消费业务逻辑有问题,现在需要重新消费。要支持此功能,则Broker存储端在向Consumer消费端投递成功消息后,消息仍然需要保留。重新消费一般是按照时间维度,例如由于Consumer系统故障,恢复后需要重新消费1小时前的数据。RocketMQ Broker提供了一种机制,可以按照时间维度来回退消费进度,这样就可以保证只要发送成功的消息,只要消息没有过期,消息始终是可以消费到的。

四、总结

本文从消息流转的整个过程分析了RocketMQ如何保证消息的可靠性,消息发送通过不同的重试策略保证了消息的可靠发送,消息存储通过不同的刷盘机制以及多副本来保证消息的可靠存储,消息消费通过至少消费成功一次以及消费重试机制来保证消息的可靠消费,RocketMQ在保证消息的可靠性上做到了全链路闭环,最大限度的保证了消息不丢失。

参考与感谢

作者 | 修戟
来源 | 阿里技术公众号
https://developer.aliyun.com/article/781629

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

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

相关文章

一文读懂“大语言模型”

1、背景 本文基于谷歌云的官方视频:《Introduction to Large Language Models》 ,使用 ChatGPT4 整理而成,希望对大家入门大语言模型有帮助。 本课程主要包括以下 4 方面的内容: 大语言模型的定义描述大语言模型的用例解释提示…

网络安全分组混战靶机攻击与加固——BPlinux系列

网络安全分组混战靶机攻击与加固——BPlinux系列 目录 一、渗透过程 二、加固过程 三、中职网络安全竞赛知识星球 一、渗透过程 #这是一个以前混战阶段用的靶机然后C模块也会用 1、我们先使用nmap --scriptvuln(这是nmap自带的脚本,可以扫描可利用的漏洞&…

计算机网络|第四章:网络层:数据平面

前文回顾:第三章:传输层 运输层依赖于网络层的主机到主机的通信服务,提供各种形式的进程到进程的通信。网络层与传输层和应用层不同的是,在网络中的每一台主机和路由器中都有一个网络层部分。正因如此,网络层协议是协议…

metaRTC+ZLMediaKit实现webrtc的推拉流

概述 ZLMediaKit是一个基于C11的高性能运营级流媒体服务框架,是一个支持webrtc SFU的优秀的流媒体服务器系统。 metaRTC新版本支持whip/whep协议,支持whip/whep协议的ZLMediaKit推拉流。 信令通信 ZLMediaKit新版本支持whip和whep协议,支…

065:cesium设置带有箭头的线材质(material-9)

第065个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中设置带有箭头的线材质,请参考源代码,了解PolylineArrowMaterialProperty的应用。 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共82行)相关API参考…

Microsoft Office 2010安装

哈喽,大家好。今天一起学习的是office2010的安装,有兴趣的小伙伴也可以来一起试试手。 一、测试演示参数 演示操作系统:Windows 7 不建议win10及以上操作系统使用 系统类型:64位 演示版本:SW_DVD5_Office_Profession…

Ceres简介及示例(9)On Derivatives(Numeric derivatives)

使用analytic derivatives的另一个极端是使用numeric derivatives。关键是,对函数f(x)关于x的求导过程可以写成极限形式: Forward Differences前向差分 当然,在计算机中,我们不能执行数值求极限操作,所以我们要做的是&#xff0…

squid的基本代理

一、Squid代理服务器的概述 squid 作为一款服务器代理工具,可以缓存网页对象,减少重复请求,从而达到加快网页访问速度,隐藏客户机真实IP,更为安全。 Squid主要提供缓存加速、应用层过滤控制的功能 1、squid代理的工…

攻击者使用“Geacon”Cobalt Strike工具瞄准macOS

威胁行为者现在正在部署一种名为 Geacon 的 Cobalt Strike 的 Go 语言实现,它于四年前首次出现在 GitHub 上。 他们正在使用红队和攻击模拟工具来针对 macOS 系统,其方式与过去几年在 Windows 平台上使用 Cobalt Strike 进行后开发活动的方式大致相同。…

Rust每日一练(Leetday0007) 删除结点、有效括号、合并链表

目录 19. 删除链表的倒数第 N 个结点 Remove-nth-node-from-end-of-list 🌟🌟 20. 有效的括号 Valid Parentheses 🌟 21. 合并两个有序链表 Mmerge-two-sorted-lists 🌟 🌟 每日一练刷题专栏 🌟 Ru…

web搭建服务器端+创建web后端项目详细步骤

一、搭建服务器端 Web服务器:用于响应来自Web客户端(如浏览器、移动应用程序等)的请求并提供Web页面和其他Web资源的软件程序或计算机系统。它允许用户在Web浏览器中输入网址,通过HTTP协议向服务器发送请求,并收到Web页…

用原生JS实现虚拟列表(IT枫斗者)

用原生JS实现虚拟列表 介绍 最近在开发需求的时候,有用到 Antd 的虚拟列表组件 rc-virtual-list ,粗略地看了一下源码,于是萌生了自己写一个虚拟列表的想法。当一个列表需要渲染大量数据的时候是非常耗时的,而且在列表滚动的过程…

Golang每日一练(leetDay0073) 实现前缀树、最短子数组

目录 208. 实现 Trie (前缀树) Implement-trie-prefix-tree 🌟🌟 209. 长度最小的子数组 Minimum-size-subarray-sum 🌟🌟 🌟 每日一练刷题专栏 🌟 Rust每日一练 专栏 Golang每日一练 专栏 Python每…

多线程的三种创建方式及各自的优缺点分析

第一种方式:继承Thread类,覆写run()方法 1、创建一个MyThread类,继承Thread类; 2、覆写run()方法,在run()方法内编写任务代码; 3、创建MyThread类,需要注意的是,如果想要给线程命名…

传染病学模型 | Matlab实现SI传染病学模型 (SI Epidemic Model)

文章目录 效果一览基本描述模型介绍程序设计参考资料效果一览 基本描述 传染病学模型 | Matlab实现SI传染病学模型 (SI Epidemic Model) 模型介绍 SI传染病模型是一种基于微分方程的流行病学模型,用于模拟传染病在人群中的传播过程。SI模型中,人群被划分为易感者(S)和感染者…

来 Azure 学习 OpenAI 四 - 用 Embedding 赋能 GPT

大家好,我是学生大使 Jambo。在我们前一篇文章中,我们介绍了 OpenAI 模型的调用。今天,我将为大家介绍 Embedding 的使用。 嵌入是什么 嵌入(Embedding )是一种将高维数据映射到低维空间的方法。嵌入可以将高维数据可…

第一章 初识Python

1.1 课前必读 课程大纲 1.2 Python介绍 Python特点: 主流语言;用途广泛,号称万能语言;上手简单; Python用途: 数据挖掘(爬虫)和数据分析自动化脚本编写(软件测试人员使用…

尚硅谷周阳老师 SpringCloud第二季学习笔记

前言:首先感谢尚硅谷周阳老师的讲解,让我对springcloud有了很好的理解,周阳老师的讲课风格真的很喜欢,内容充实也很幽默,随口一说就是一个段子,我也算是周阳老师的忠实粉丝啦。 先说说课程总体内容 以下是…

[学习笔记] [机器学习] 6. [上]决策树算法(熵Entropy、信息增益(率)、基尼值(指数)、CART剪枝、特征工程特征提取、回归决策树)

视频链接数据集下载地址:无需下载 学习目标: 掌握决策树实现过程知道信息熵的公式以及作用知道信息增益、信息增益率和基尼指数的作用知道id3、c4.5、cart算法的区别了解cart剪枝的作用知道特征提取的作用应用DecisionTreeClassifier实现决策树分类 1…

开放原子训练营(第三季)inBuilder低代码开发实验室,低代码到底该长什么样

目录 前言: 一、什么是inBuilder低代码开发实验室 二、技术特征 2.1开放性 2.2开发语言无关性 2.3云原生 2.4模型工程化 2.5全栈模型刻画 2.6运行态定制 2.7仓库介绍 三、快速入门 四、实操案例 4.1定义数据源 4.2 设计页面 4.3发布调试 五、总结 前言&#xf…