「消息队列原理与实战」 | 青训营笔记

185 阅读24分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 13 天

本节课程重点

主要分为五个方面:

  1. 消息队列的前世今生
  1. 消息队列-Kafka
  2. 消息队列-BMQ
  3. 消息队列-RocketMQ

对于消息队列来说,在互联网的各种业务中发挥着至关重要的作用,但是在大学的课程中却少有提及,因此希望通过此次分享帮助大家去了解工业界比较成熟消息队列的底层原理、架构设计、以及它们的一些高级特性,让同学在以后的学习中或者工作中更加熟练地去使用消息队列或者进行技术选型

1.消息队列是什么

1.1 问题引出

假如有一天晚上晚上我们上完课,回到宿舍,想着新出的游戏机,但又摸了摸钱包,太贵了买不起,这个时候你突然想到,今天抖音直播搞活动,瞬间你掏出了手机打开抖音搜索,找到直播间以后,你点开了心心念念的游戏机详情页,看到价格只要 500。这个时候我们分析一下,就我们上面这几步操作,在我们的程序背后,做了什么事情?

image-20230209114008631.png

1.1.1 问题一:系统崩溃

如果此时记录存储程序所在的机房被删库跑路了,上面这个流程会发生什么问题?

1.1.2 问题二:服务能力有限

看到这个价格,你非常心动,定晴看,商品即将在 3 分钟后开抢,这个价格必须要抢到啊!但此时在无数台手机的后面,藏着无数和你一样饥渴的同学,时间快到了,心里面想着赶紧抢,我们再来看看,后面的程序又做了哪些事情呢?

可以看到,一堆人都发起了订单请求,可是公司给的预算不够,服务器的配置太弱,订单服务只能同事处理 10 个订单请求。这个时候我们又该怎么办呢?

image-20230209114414553.png

1.1.3 问题三:链路耗时长

在我们点击提交订单之后,这个怎么一直转圈圈,卡在这个页面啊,等了半分钟后,啊终于抢到了,不过这个 app 也太慢了,下次不用了。我们进一步看一下这次问题出在哪里了?

一通分析,发现,库存服务和订单服务都挺快的,但是最后通知商家这一步咋这么慢,是不是还可以进行优化呀?对于这个流程应该怎么优化来挽回这个暴躁的用户?

image-20230209114903714.png

1.1.4 问题四:日志存储

最终,抢到以后,有点兴奋,但看看时间,该睡觉了。。。

在大家都抢到了自己心仪商品准备睡去的时候,在字节跳动的会议室里传出了悲伤的声音,因为刚刚有服务器坏掉了,我们的本地日志都丢掉了,没有了日志,我们还怎么去修复那些刚刚出现的那些问题,周围一片寂静,突然小张站出来缓缀的说了一句话,众人才露出了微笑准备下班离开,大家能猜到小张到底说的什么吗?

1.2 解决方案

1.2.1 解耦

面对存储行为服务崩溃的时候

image-20230209115449688.png

1.2.2 削峰

当服务能力有限时

image-20230209115536138.png

1.2.3 异步

面对链路耗时长的问题

image-20230209115605169.png

1.2.4 日志处理

面对日志存储问题

image-20230209115716736.png

1.3 概念

消息队列( MQ ),指保存消息的一个容器,本质是个队列。但这个队列呢,需要支持高吞吐,高并发,并且高可用

image-20230209115912940.png

2.消息队列的前世今生

image-20230209120045365.png

消息中间件其实诞生的很早,早在 1983 年互联网应用还是一片荒芜的年代,有个在美国的印度小哥 Vivek 就设想了一种通用软件总线,世界上第一个现代消息队列软件 The Information Bus(TIB), TB 受到了企业的欢迎,这家公司的业务发展引起了当时最牛气的 IT 公司 IBM 的注意,于是他们一开始研发了自己消息队列软件,于是才有了后来的 wesphere mq ,再后来微软也加入了战团。接近 2000 年的时候,互联网时代已经初见曙光,全球的应用程序得到了极大地丰富,对于程序之间互联互通的需求越来越强烈,但是各大 IT 公司之间还是牢牢建立着各种技术壁垒,以此来保证自己的商业利益,所以消息中间件在那个时候是大型企业才能够用的起的高级玩意。

但是时代的洪流不可逆转,有壁垒就有打破壁垒的后来者,2001 年 sun 发布了 jms 技术,试图在各大厂商的层面上再包装一层统一的 java 规范。java 程序只需要针对 jms api 编程就可以了,不需要再关注使用了什么样的消息中间件,但是 jms 仅仅适用于j ava。2004 年 AMQP (高级消息队列协议)诞生了,才是真正促进了消息队列的繁荣发展,任何人都可以针对 AMQP 的标准进行编码。有好的协议指导,再加上互联网分布式应用的迅猛发展成为了消息中间件一飞冲天的最大动力,程序应用的互联互通,发布订阅,最大契合了消息中间件的最初的设计初衷。

除了刚才介绍过的收费中间件,后来开源消息中间件开始层出不穷,常见比较流行的有 ActiveMQ、RabbitMQ、Kafak、阿里的 RocketMQ,以及目前存算分离的 Pulsar ,在目前互联网应用中消息队列中间件基本上成为标配。

业界消息队列对比:

image-20230209120733735.png

3.消息队列-Kafka

3.1 使用场景

kafka 使用场景,业务日志、用户行为数据、Metrics 数据

image-20230209152212843.png

3.2 如何使用 Kafka

image-20230209152321233.png

第一步:首先需要创建一个 Kafka 集群,但如果你是在字节工作,恭喜你这一步消息团队的小伙伴已经帮你完成了

第二步:需要在这个集群中创建一个 Topic,并且设置好分片数量

第三步:引入对应语言的 SDK,配置好集群和 Topics 等参数,初始化一个生产者,调用 Send 方法,将你的 Hello World 发送出去

第四步:引入对应语言的 SDK,配置好集群和 Topics 等参数,初始化一个消费者,调用 Poll 方法,你将收到你刚发送的 Hello World

3.3 基本概念

image-20230209152734595.png

Topic: Kakfa 中的逻辑队列,可以理解成每一个不同的业务场景就是一个不同的 topic ,对于这个业务来说,所有的数据都存储在这个 topic 中

Cluster: Kafka 的物理集群,每个集群中可以新建多个不同的 topic

Producer: 顾名思义,也就是消息的生产端,负责将业务消息发送到 Topic 当中

Consumer: 消息的消费端,负责消费已经发送到 topic 中的消息

ConsumerGroup: 消费者组,不同组 Consumer 消费进度互不干涉

Partition: 通常 topic 会有多个分片,不同分片直接消息是可以并发来处理的,这样提高单个 Topic 的吞吐能力

3.3.1 Offset

Offset: 消息在 partition 内的相对位置信息,可以理解为唯一 ID,在 partition 内部严格递增。

image-20230209153728827.png

3.3.2 Replica

每个分片有多个 Replica,Leader Replica 将会从 ISR 中选出。

image-20230209153815216.png

Replica: 分片的副本,分布在不同的机器上,可用来容灾,Leader 对外服务,Follower 异步去拉取 leader 的数据进行一个同步,如果 leader 挂掉 了,可以将 Follower 提升成 leader 再堆外进行服务

ISR: 意思是同步中的副本,对于 Follower 来说,始终和 leader 是有定差距的,但当这个差距比较小的时候,我们就可以将这个 follower 副本加入到 ISR 中,不在 ISR 中的本是不允许提升成 Leader 的

3.4 数据复制

下面这幅图代表着 Kafka 中副本的分布图。途中 Broker 代表每一个 Kafka 的节点,所有的 Broker 节点最终组成了一个集群。整个图表示,图中整个集群,包含了 4个 Broker 机器节点,集群有两个 Topic,分别是 Topic1 和 Topic2 , Topic1 有两个分片,Topic2 有 1 个分片,每个分片都是三副本的状态。这里中间有一个 Broker 同时也扮演了 Controller 的角色,Controller 是整个集群的大脑,负责对副本和 Broker 进行分配

image-20230209154133787.png

3.5 Kafka架构

ZooKeeper :负责存储集群元信息,包括分区分配信息等

image-20230209154253047.png

而在集群的基础上,还有一个模块是 ZooKeeper ,这个模块其实是存储了集群的元数据信息,比如副本的分配信息等等,Controller 计算好的方案都会放到这个地方

3.6 一条消息的自述

了解完成整个 Kafka 的基本概念和架构之后,我们从一条消息的视角来看看完整的处理流程,了解一下 Kafka 为什么可以支撑如此高的吞吐?

image-20230209154320706.png 如果发送一条消息,等到其成功后再发一条会有什么问题?

image-20230209162345869.png

3.7 Producer

3.7.1 批量发送

image-20230209162505742.png 思考题:如果消息量很大,网络带宽不够用,如何解决?

3.7.2 数据压缩

image-20230209162651004.png

3.8 Broker

3.8.1 数据的存储

如何写入到磁盘呢,我们先来看一下 Kafka 最终存储的文件结构是什么样子的?

image-20230209162728304.png

3.8.2 消息文件结构

在每一个 Broker ,都分布着不同 Topic 的不同分片,

image-20230209162927519.png

3.8.3 磁盘结构

移动磁头找到对应磁道,磁盘转动,找到对应扇区,最后写入。寻道成本比较高,因此顺序写可以减少寻道所带来的时间成本。

image-20230209163048117.png

只看一个盘面,磁头>磁道->扇区寻道

3.8.4 顺序写

采用顺序写的方式进行写入,以提高写入效率

image-20230209163144191.png

3.8.5 如何找到消息

此时我们的消息写入到 Broker 的磁盘上了,然后 Consumer 会通过发送 FetchRequest 请求消息数据,Broker 将指定 Offset 处的消息,按照时间窗口和消息大小窗口发送给 Consumer,寻找数据这个细节是如何做到的呢?

image-20230209163254085.png

3.8.6 偏移量索引文件

image-20230209163339604.png

介绍文件:文件名是文件中第一条消息的 offset ,

然后,第一步,通过二分找到小于目标文件的最大文件

通过二分找到小于目标 offset 最大的索引位置,再遍历找到目标 offset

image-20230209163526378.png

3.8.7 时间戳索引文件

image-20230209163947483.png

如果我们需要使用时间戳来寻找的时候,和 offset 相比只是多加了一级索引,也就是通过二分找到时间戳对应的offset,再重复之前的步骤找到相应的文件数据

3.8.8 传统数据拷贝

image-20230209164141074.png

3.8.9 零拷贝

image-20230209164317022.png

Consumer 从 Broker 中读取数据,通过 sendfile 的方式,将磁盘读到 os 内核缓冲区后,直接转到 socket buffer 进行网络发送。

Producer 生产的数据持久化到broker,采用 mmap 文件映射,实现顺序的快速写入

3.9 Consumer

3.9.1 消息的接收端

如何解决 Partition 在 Consumer Group 中的分配问题?

image-20230209165456076.png

对于一个 Consumer Group 来说,多个分片可以并发的消费,这样可以大大提高消费的效率,但需要解决的问题是,Consumer 和 Partition 的分配问题,也就是对于每一个 Partition 来讲,该由哪一个 Consumer 来消费的问题。对于这个问题,我们一般有两种解决方法,手动分配和自动分配

3.9.2 Low Level

通过手动进行分配,哪一个 Consumer 消费哪一个 Partition 完全由业务来决定。

image-20230209165733681.png

第一,手动分配,也就是 Kafka 中所说的 Low Level 消费方式进行消费,这种分配方式的一个好处就是启动比较快,因为对于每一个 Consumer 来说,启动的时候就已经知道了自己应该去消费哪个消费方式,就好比图中的 Consumer Group1 来说 Consumer1 去消费 Partition1,2,3 ;Consumer2 去消费 4,5,6;Consumer3 去消费 7,8。这些 Consumer 在启动的时候就已经知道分配方案了。

但这样这种方式的缺点又是什么呢,想象一下,如果我们的 Consumer3 挂掉了,我们的 7,8 分片是不是就停止消费了。又或者,如果我们新增了一台 Consumer4 ,那是不是又需要停掉整个集群,重新修改配置再上线,保证 Consumer4 也可以消费数据,其实上面两个问题,有时候对于线上业务来说是致命的。

3.9.3 Hight Level

image-20230209165858580.png

所以 Kafka 也提供了自动分配的方式,这里也叫做 High Level 的消费方式,简单的来说,就是在我们的 Broker 集群中,对于不同的 Consumer Group 来讲,都会选取台 Broker 当做 Coordinator ,而 Coordinator 的作用就是帮助 Consumer Group 进行分片的分配,也叫做分片 rebalance,使用这种方式,如果 ConsumerGroup 中有发生宕机,或者有新的 Consumer 加入,整个 partition 和 Consumer 都会重新进行分配来达到一个稳定的消费状态

3.10 Consumer Rebalance

接下来我们深入了解一下 Rebalance 的具体步骤吧

image-20230209170117427.png

3.11 Kafka-数据复制问题

image-20230209154133787.png

通过前面的介绍我们可以知道,对于 Kafka 来说,每一个 Broker 上都有不同 topic 分区的不同副本,而每一个副本,会将其数据存储到该 Kafka 节点上面,对于不同的节点之间,通过副本直接的数据复制,来保证数据的最终一致性,与集群的高可用。

3.12 Kafka-重启操作

image-20230209170736947.png

举个例子来说,如果我们对一个机器进行重启,首先,我们会关闭一个 Broker ,此时如果该 Broker 上存在副本的 Leader,那么该副本将发生 leader 切换,切换到其他节点上面并且在 ISR 中的 Follower 副本,可以看到图中是切换到了第二个 Broker 上面

而此时,因为数据在不断的写入,对于刚刚关闭重启的 Broker 来说,和新 Leader 之间一定会存在数据的滞后,此时这个 Broker 会追赶数据,重新加入到 ISR 当中

当数据追赶完成之后,我们需要回切 leader ,这一步叫做 prefer leader,这一步的目的是为了避免在一个集群长期运行后,所有的 leader 都分布在少数节点上,导致数据的不均衡

通过上面的一个流程分析,我们可以发现对于一个 Broker 的重启来说,需要进行数据复制,所以时间成本会比较大,比如一个节点重启需要 10 分钟,一个集群有 1000 个节点,如果该集群需要重启升级,则需要 10000 分钟,那差不多就是一个星期,这样的时间成本是非常大的。

有同学可能会说,老师可以不可以并发多台重启呀,问的好,不可以。为什么呢,在一个两副本的集群中,重启了两台机器,对某一分片来讲,可能两个分片都在这台机器上面,则会导致该集群处于不可用的状态。这是更不能接受的。

3.13 Kafka-替换、扩容、缩容

image-20230209171013349.png

如果是替换,或者扩容,或者缩容操作呢,我们来看看。

如果是替换,和刚刚的重启有什么区别,其实替换,本质上来讲就是一个需要追更多数据的重启操作,因为正常重启只需要追一小部分,而替换,则是需要复制整个 leader 的数据,时间会更长

扩容呢,当分片分配到新的机器上以后,也是相当于要从 0 开始复制一些新的副本

而缩容,缩容节点上面的分片也会分片到集群中剩余节点上面,分配过去的副本也会从 0 开始去复制数据

以上三个操作均有数据复制所带来的时间成本问题,所以对于 Kafka 来说,运维操作所带来的时间成本是不容忽视的

3.14 Kafka-负载不均衡

image-20230209171127139.png

这个场景当中,同一个 Topic 有 4 个分片,两副本,可以看到,对于分片 1 来说,数据量是明显比其他分片要大的,当我们机器 IO 达到瓶颈的时候,可能就需要把第一台 Broker 上面的 Partition3 迁移到其他负载小的 Broker 上面,接着往下看

image-20230209171252609.png

但我们的数据复制又会引起 Broker1 的 IO 升高,所以问题就变成了,我为了去解决 IO 升高,但解决问题的过程又会带来更高的 IO ,所以就需要权衡 IO 设计出一个极其复杂的负载均衡策略

Kafka-问题总结

  1. 运维成本高
  2. 对于负载不均衡的场景,解决方案复杂
  3. 没有自己的缓存,完全依赖 Page Cache
  4. Controller 和 Coordinator 和 Broker 在同一进程中,大量 IO 会造成其性能下降

4.消息队列-BMQ

4.1 BMQ 介绍

BMQ 兼容 Kafka 协议,存算分离,云原生消息队列,初期定位是承接高吞吐的离线业务场景,逐步替换掉对应的 Kafka 集群,我们来了解一下 BMQ 的架构特点

image-20230209181551315.png

Producer -> Consumer -> Proxy -> Broker -> HDFS -> Controller -> Coordinator -> Meta

着重强调一下 Proxy 和 Broker 无状态,为下面运维比较做铺垫。这里简单介绍一下存算分离,适配 Kafka 协议,为什么不选择 Pulsar 的原因

4.2 运维操作对比

image-20230209181709939.png

实际上对于所有节点变更的操作,都仅仅只是集群元数据的变化,通常情况下都能秒级完成,而真正的数据已经移到下层分布式文件存储去了,所以运维操作不需要额外关心数据复制所带来的时间成本

4.3 HDFS 写文件流程

image-20230209181747078.png

通过前面的介绍,我们知道了,同一个副本是由多个 segment 组成,我们来看看 BMQ 对于单个文件写入的机制是怎么样的,首先客户端写入前会选择一定数量的 DataNode ,这个数量是副本数,然后将一个文件写入到这三个节点上,切换到下一个 segment ,之后,又会重新选择三个节点进行写入。这样一来,对于单个副本的所有 segment 来讲,会随机的分配到分布式文件系统的整个集群中

4.4 BMQ 文件结构

image-20230209181827339.png

对于 Kafka 分片数据的写入,是通过先在 Leader 上面写好文件,然后同步到 Follower 上,所以对于同一个副本的所有 Segment 都在同一台机器上 面。就会存在之前我们所说到的单分片过大导致负载不均衡的问题,但在 BMQ 集群中,因为对于单个副本来讲,是随机分配到不同的节点上面的,因此不会存在 Kafka 的负载不均问题

4.5 Broker

4.5.1 Partition 状态机

保证对于任意分片在同一时刻只能在一个 Broker 上存活

image-20230209181858300.png

其实对于写入的逻辑来说,我们还有个状态机的机制,用来保证不会出现同一个分片在两个 Broker 上同时启动的情况,另外也能够保证一个分片的正常运行。首先,Controller 做好分片的分配之后,如果在该 Broker 分配到了 Broker,首先会 start 这个分片,然后进入 Recover 状态,这个状态主要有两个目的获取分片写入权利,也就是说,对于 hdfs 来讲,只会允许我一个分片进行写入,只有拿到这个权利的分片我才能写入,第二个目的是如果上次分片是异常中断的,没有进行 save checkpoint ,这里会重新进行一次 save checkpoint ,然后就进入了正常的写流程状态,创建文件,写入数据,到一定大小之后又开始建立新的文件进行写入。

4.5.2 写文件流程

image-20230209181916580.png

数据校验:CRC,参数是否合法校验完成后,会把数据放入 Buffer 中,通过一个异步的 Write Thread 线程将数据最终写入到底层的存储系统当中

这里有一个地方需要注意一下,就是对于业务的写入来说,可以配置返回方式,可以在写完缓存之后直接返回,另外我也可以数据真正写入存储系统后再返回,对于这两个来说前者损失了数据的可靠性,带来了吞吐性能的优势,因为只写入 内存是比较快的,但如果在下一次 flush 前发生宕机了,这个时候数据就有可能丢失了,后者的话,因为数据已经写入了存储系统,这个时候也不需要担心数据丢失,相应的来说吞吐就会小一些

4.5.3 写文件 Failover

如果 DataNode 节点挂了或者是其他原因导致我们写文件失败,应该如何处理?

image-20230209181934555.png

我们之前说到了,建立一个新的文件,会随机挑选与副本数量相当的数据节点进行写入,那如果此时我们挑选节点中有一个出现了问题,导致不能正常写入了,我们应该怎么处理是需要在这里等着这个节点恢复吗,当然不行,谁知道这个节点什么恢复,既然你不行,那就把你换了,可以重新找正常的节点创建新的文件进行写入,这样也就保证了我们写入的可用性

4.6 Proxy

image-20230209182014045.png

首先 Consumer 发送一个 Fetch Request,然后会有一个 Wait 流程,那么他的左右是什么呢,想象一个 Topic ,如果一直没有数据写入,那么,此时 consumer 就会一直发送 Fetch Request,如果 Consumer 数量过多,BMQ 的 server 端是扛不住这个请求的,因此,我们设置了一个等待机制,如果没有 fetch 到指定大小的数据,那么 proxy 会等待一定的时间,再返回给用户侧,这样也就降低了 fetch 请求的 IO 次数,经过我们的 wait 流程后,我们会到我们的 Cache 里面去找到是否有存在我们想要的数据,如果有直接返回,如果没有,再开始去存储系统当中寻找,首先会 Open 这个文件,然后通过 Index 找到数据所在的具体位置,从这个位置开始读取数据

4.7 多机房部署

image-20230209182046950.png

为什么需要多机房部署,其实对于一个高可用的服务,除了要防止单机故障所带来的的影响意外,也要防止机房级故障所带来的影响,比如机房断点,机房之间网络故障等等。那我们来看看 BMQ 的多机房部署是怎么做

Proxy -> Broker -> Meta -> HDFS

4.8 BMQ-高级特性

image-20230209182204021.png

泳道 -> Databus -> Mirror -> Index -> Parquet

4.9 泳道消息

BOE: Bytedance Offline Environment,是一套完全独立的线下机房环境 PPE: Product Preview Environment,即产品预览环境

image-20230209182233024.png 多个人同时测试,需要等特上一个人测试完成:

image-20230209182301243.png 每多一个测试人员,都需要重新搭建一个相同配置的 Topc,造成人力和资源的浪费:

image-20230209182625557.png 对于 PPE 的消费者来说,资源没有生产环境多,所以无法承受生产环境的流量:

image-20230209182708471.png 解决主干泳道流量隔离问题以及泳道资源重复创建问题:

image-20230209182756324.png

4.10 Databus

image-20230209182832479.png 直接使用原生 SDK 会有什么问题?

1.客户端配置较为复杂 2.不支持动态配置,更改配置需要停掉服务 3.对于 latency 不是很敏感的业务,batch 效果不佳

image-20230209182850297.png 1.简化消息队列客户瑞复杂度 2.解耦业务与Topic 3.缓解集群压力,提高吞吐

4.11 Mirror

image-20230209182909503.png 思考一下,我们是否可以通过多机房部署的方式,解决跨 Region 读写的问题?

image-20230209182933210.png 使用 Mirror (镜像)通过最终一致的方式,解决跨 Region 读写问题。

4.12 Index

image-20230209183035826.png 如果希望通过写入的 Logld、UserId 或者其他的业务字段进行消息的查询,应该怎么做?

image-20230209183049108.png 直接在 BMO 中将数据结构化,配置索引 DDL,异步构建索引后,通过 Index Query 服务读出数据。

4.13 Parquet

Apache Parquet 是 Hadoop 生态圈中一种新型列式存储格式,它可以兼容 Hadoop 生态圈中大多数计算框架( Hadoop、Spark 等),被多种查询引擎支持(Hive、Impala、Drill等)。

image-20230209183137240.png 直接在 BMQ 中将数据结构化,通过 Parquet Engine,可以使用不同的方式构建 Parquet 格式文件。

image-20230209183213144.png

小结

l. BMQ 的架构模型(解决 Kafka 存在的问题)

  1. BMO 读写流程( Failover 机制,写入状态机)
  2. BMQ 高级特性(泳道、Databus、Mirror、Index、Parquet)

5.消息队列-RocketMQ

5.1 RocketMQ 使用场景

例如,针对电商业务线,其业务涉及广泛,如注册、订单、库存、物流等:同时,也会涉及许多业务峰值时刻,如秒杀活动、周年庆、定期特惠等

5.2 RocketMQ 基本概念

image-20230209183336643.png

image-20230209183502246.png

根据我们刚刚的介绍,可以看到 Producer,Consumer,Broker 这三个部分,Kafka 和 RocketMQ 是一样的,而 Kafka 中的 Partition 概念在这里叫做 ConsumerQueue,

5.3 RocketMQ 架构

image-20230209183544429.png

先说数据流也,是通过 Producer 发送给 Broker 集群,再由 Consumer 进行消费

Broker 节点有 Master 和 Slave 的概念 NameServer 为集群提供轻量级服务发现和路由

5.4 存储模型

image-20230209183620703.png

接下来我们来看看 RocketMQ 消息的存储模型,对于一个 Broker 来说所有的消息的会 append 到一个 CommitLog 上面,然后按照不同的 Queue ,重新 Dispatch 到不同的 Consumer 中,这样 Consumer 就可以按照 Queue 进行拉取消费,但需要注意的是,这里的 ConsumerQueue 所存储的并不是真实的数据,真实的数据其实只存在 CommitLog 中,这里存的仅仅是这个 Queue 所有消息在 CommitLog 上面的位置,相当于是这个 Queue 的一个密集索引

5.5 RocketMQ-高级特性

5.5.1 事务场景

image-20230209183639250.png

先看一下我们最开始说的这个场景,正常情况下,这个下单的流程应该是这个样子,首先我保证库存足够能够顺利 -1 ,这个时候再消息队列让我其他系统来处理,比如订单系统和商家系统,但这里有个比较重要的点,我库存服务和消息队列必须要是在同一个事务内的,大家还记不记得事务的基本特性是什么。 ACID ,这里库存记录和往消息队列里面发的消息这两个事情,是需要有事务保证的,这样不至于发生,库存已经 -1 了,但我的订单没有增加,或者商家也没有收到通知要发货。因此 RocketMQ 提供事务消息来保证类似的场景,我们来看看其原理是怎么样的

5.5.2 事物消息

image-20230209183717356.png

5.5.3 延迟发送

image-20230209183733679.png

image-20230209183820436.png

5.5.4 延迟消息

image-20230209183910437.png

5.5.5 处理失败

该如何处理失败的消息呢?

image-20230209183935570.png

5.5.6 消费重试和死信队列

image-20230209183925948.png

小结

  1. RocketMQ 的基本概念(Queue,Tag)
  1. RocketMQ 的底层原理(架构模型、存储模型)
  1. RocketMQ 的高级特性(事务消息、重试和死信队列,延迟队列)

课程总结

  1. 前世今生:消息队列发展历程
  1. Kafka:基本概念、架构设计、底层原理、架构缺点
  1. BMQ:架构设计、底层原理、Kafka 比较、高级特性
  1. RocketMO:架构设计、底层原理、高级特性

课后

  1. 消息队列的应用场景有哪些?
  1. Kafka的哪些Feature让其可以支撑大吞吐写入的场景?
  1. Kafka Consumer Rebalance的流程简述?
  1. BMQ相比较Kafka有哪些优势?
  1. RocketMQ有哪些特有的Feature?
  1. RocketMQ事务消息处理流程简述?
  1. 你认为MQ后面应该如何发展?(开放题)

个人总结

  • 了解消息队列的原理和作用
  • 在开发中应用消息队列处理业务

引用参考

后端专场 学习资料五