RocketMQ 是什么?它的架构是怎么样的?和 Kafka 又有什么区别?
声明:本文基于一位up主-小白debug的文章进行了延伸和拓展,用于学习。
RocketMQ 是什么?
RocketMQ 是阿里自研的国产消息队列,目前已经是 Apache 的顶级项目。和其他消息队列一样,它接受来自生产者的消息,将消息分类,每一类是一个 topic,消费者根据需要订阅 topic,获取里面的消息。
RocketMQ大致架构如下:

RocketMQ 四大核心组件作用与核心功能
RocketMQ 架构由 NameServer、Broker、Producer、Consumer 四大核心组件构成,协同实现消息的生产、存储、分发与消费,以下是各组件的详细说明:
1. NameServer
作用
作为整个 RocketMQ 集群的 “路由中心”,管理 Broker 节点的注册与发现,以及提供 Topic 路由信息查询服务。
核心功能
- Broker 注册与心跳检测
- Broker 启动时会向所有 NameServer 注册自身信息(IP、端口、Topic 列表等)。
- 注册后定期发送心跳包维持连接,NameServer 若长时间未收到心跳,会将该 Broker 节点下线。
- 路由信息管理
- 维护整个集群的 Topic 路由表,记录每个 Topic 对应的所有 Broker 节点及队列信息。
- 无状态设计
- 集群中各节点地位平等,互不通信,数据独立存储在内存中。
- 优势:集群易扩展,单个节点故障不影响整体可用性。
2. Broker
作用
RocketMQ 的 “核心存储与转发节点”,负责消息的接收、存储、持久化和投递。
核心功能
- 消息存储
- 接收 Producer 发送的消息,存储在本地磁盘(默认路径:
${ROCKETMQ_HOME}/store)。 - 支持两种刷盘机制:
- 同步刷盘:消息写入磁盘成功后才返回响应,数据无丢失,但性能略低。
- 异步刷盘:消息写入 PageCache 后即返回响应,后台线程批量刷盘,性能更高,极端情况可能丢失数据。
- 接收 Producer 发送的消息,存储在本地磁盘(默认路径:
- 消息转发
- 为 Consumer 提供消息拉取服务,根据 Consumer 的 Offset 从磁盘/PageCache 读取消息并返回。
- 高可用设计
- 支持主从架构:
- Master 节点:处理消息写入和读取请求。
- Slave 节点:同步 Master 数据,Master 故障时可切换为 Master 继续服务。
- 一个 Topic 的不同队列可分布在多个 Broker 上,实现负载均衡与容错。
- 支持主从架构:
- 消息过滤
- 支持 Tag 过滤和 SQL92 过滤,Consumer 仅订阅感兴趣的消息,减少网络传输和处理开销。
3. Producer
作用
消息的 “生产者”,负责创建消息并发送到 Broker。
核心功能
- 消息构造
- 构建消息对象,设置 Topic、Tag、Key、Body 等核心字段。
- 负载均衡
- 从 NameServer 获取 Topic 对应的路由信息,通过默认轮询算法选择目标 Broker 的队列发送消息。
- 重试机制
- 消息发送失败(如网络异常、Broker 繁忙)时,自动重试(默认重试 2 次),确保消息尽可能送达。
- 事务消息支持
- 支持分布式事务消息,通过二阶段提交机制保证事务最终一致性。
4. Consumer
作用
消息的 “消费者”,负责从 Broker 拉取消息并进行业务处理。
核心功能
- 消息拉取/推送
- 拉取模式(Pull):Consumer 主动向 Broker 请求拉取消息。
- 推送模式(Push):底层基于拉取实现(长轮询优化实时性),Broker 主动推送消息给 Consumer。
- 消费组管理
- 多个 Consumer 可组成消费组(Consumer Group),共同消费同一个 Topic 的消息。
- 组内 Consumer 分摊消费队列,避免重复消费(广播模式除外)。
- Offset 管理
- 记录每个队列的消费进度(Offset),Consumer 重启后可从上次消费位置继续拉取。
- 存储方式:
- 默认:存储在 Broker 的
consumerOffset.json文件中。 - 支持自定义存储(如数据库、Redis)。
- 默认:存储在 Broker 的
- 消息重试与死信队列
- 消费失败的消息放入重试队列,Consumer 定时重试消费。
- 多次重试失败的消息移入死信队列(DLQ),避免影响正常消息消费。
流程图如下:
是不是很像 Kafka,那么问题很自然就来了,既然都是消息队列,那它们之间有什么区别呢?
RocketMQ 和 Kafka 的区别
RocketMQ 的架构其实参考了 Kafka 的设计思想,同时又在 Kafka 的基础上做了一些调整。
这些调整,用一句话总结就是,“和 Kafka 相比,RocketMQ 在架构上做了减法,在功能上做了加法”。我们来看下这句话的含义。
在架构上做减法
我们来简单回顾下消息队列 Kafka 的架构。
kakfa 也是通过多个 topic 对消息进行分类。

- • 为了提升单个 topic 的并发性能,将单个 topic 拆为多个
partition。

- • 为了提升系统扩展性,将多个 partition 分别部署在不同
broker上。 - • 为了提升系统的可用性,为 partition 加了多个副本。
- • 为了协调和管理 Kafka 集群的数据信息,引入
Zookeeper作为协调节点。

如果你对这些依旧很陌生,可以看看上篇文章《Kafka 是什么》。
Kafka 已经是非常强的消息队列了,我们来看下 RocketMQ 在 Kafka 架构的基础上,还能玩出什么花样来。
简化协调节点
Zookeeper 在 Kafka 架构中会和 broker 通信,维护 Kafka 集群信息。一个新的 broker 连上 Zookeeper 后,其他 broker 就能立马感知到它的加入,像这种能在分布式环境下,让多个实例同时获取到同一份信息的服务,就是所谓的分布式协调服务。

但 Zookeeper 作为一个通用的分布式协调服务,它不仅可以用于服务注册与发现,还可以用于分布式锁、配置管理等场景。Kafka 其实只用到了它的部分功能,多少有点杀鸡用牛刀的味道。太重了。
所以 RocketMQ 直接将 Zookeeper 去掉,换成了 nameserver,用一种更轻量的方式,管理消息队列的集群信息。生产者通过 nameserver 获取到 topic 和 broker 的路由信息,然后再与 broker 通信,实现服务发现和负载均衡的效果。

当然,开发 Kafka 的大佬们后来也意识到了 Zookeeper 过重的问题,所以从 2.8.0 版本就支持将 Zookeeper 移除,通过 在 broker 之间加入一致性算法 raft 实现同样的效果,这就是所谓的 KRaft 或 Quorum 模式。

简化分区
我们知道,Kafka 会将 topic 拆分为多个 partition,用来提升并发性能。

在 RocketMQ 里也一样,将 topic 拆分成了多个分区,但换了个名字,叫 Queue,也就是"队列"。

Kafka 中的 partition 会存储完整的消息体,而 RocketMQ 的 Queue 上却只存一些简要信息,比如消息偏移 offset,而消息的完整数据则放到"一个"叫 commitlog 的文件上,通过 offset 我们可以定位到 commitlog 上的某条消息。
Kafka 消费消息,broker 只需要直接从 partition 读取消息返回就好,也就是读第一次就够了。

而在 RocketMQ 中,broker 则需要先从 Queue 上读取到 offset 的值,再跑到 commitlog 上将完整数据读出来,也就是需要读两次。

那么问题就来了,看起来 Kafka 的设计更高效?为什么 RocketMQ 不采用 Kafka 的设计?
这就不得说一下 Kafka 的底层存储了。
Kafka 的底层存储
Kafka 的 partition 分区,其实在底层由很多段(segment)组成,每个 segment 可以认为就是个小文件。将消息数据写入到 partition 分区,本质上就是将数据写入到某个 segment 文件下。

我们知道,操作系统的机械磁盘,顺序写的性能会比随机写快很多,差距高达几十倍。为了提升性能,Kafka 对每个小文件都是顺序写。
如果只有一个 segment 文件,那写文件的性能会很好。
但当 topic 变多之后,topic 底下的 partition 分区也会变多,对应的 partition 底下的 segment 文件也会变多。同时写多个 topic 底下的 partition,就是同时写多个文件,虽然每个文件内部都是顺序写,但多个文件存放在磁盘的不同地方,原本顺序写磁盘就可能劣化变成了随机写。于是写性能就降低了。

那问题又又来了,究竟多少 topic 才算多?这个看实际情况,但打太极从来不是我的风格。
我给一个经验值仅供参考,8 个分区的情况下,超过 64 topic, Kafka 性能就会开始下降。
RocketMQ 的底层存储
为了缓解同时写多个文件带来的随机写问题,RocketMQ 索性将单个 broker 底下的多个 topic 数据,全都写到"一个"逻辑文件 CommitLog 上,这就消除了随机写多文件的问题,将所有写操作都变成了顺序写。大大提升了 RocketMQ 在多 topic 场景下的写性能。

注意上面提到的"一个"是带引号的,虽然逻辑上它是一个大文件,但实际上这个 CommitLog 由多个小文件组成。每个文件的大小是固定的,当一个文件被写满后,会创建一个新的文件来继续存储新的消息。这种方式可以方便地管理和清理旧的消息。
简化备份模型
我们知道,Kafka 会将 partiton 分散到多个 broker 中,并为 partiton 配置副本,将 partiton 分为 leader和 follower,也就是主和从。broker 中既可能有 A topic 的主 partiton,也可能有 B topic 的从 partiton。
主从 partiton 之间会建立数据同步,本质上就是同步 partiton 底下的 segment 文件数据

RocketMQ 将 broker 上的所有 topic 数据到写到 CommitLog 上。如果还像 Kafka 那样给每个分区单独建立同步通信,就还得将 CommitLog 里的内容拆开,这就还是退化为随机读了。
于是 RocketMQ 索性以 broker 为单位区分主从,主从之间同步 CommitLog 文件,保持高可用的同时,也大大简化了备份模型。

好了,到这里,我们熟悉的 Kafka 架构,就成了 RocketMQ 的架构。

是不是跟 Kafka 的很像但又简化了不少?

在功能上做加法
虽然 RocketMQ 的架构比 Kafka 的简单,但功能却比 Kafka 要更丰富,我们来看下。
消息过滤
我们知道,Kafka 支持通过 topic 将数据进行分类,比如订单数据和用户数据是两个不同的 topic,但如果我还想再进一步分类呢?比如同样是用户数据,还能根据 vip 等级进一步分类。假设我们只需要获取 vip6 的用户数据,在 Kafka 里,消费者需要消费 topic 为用户数据的所有消息,再将 vip6 的用户过滤出来。
而 RocketMQ 支持对消息打上标记,也就是打 tag,消费者能根据 tag 过滤所需要的数据。比如我们可以在部分消息上标记 tag=vip6,这样消费者就能只获取这部分数据,省下了消费者过滤数据时的资源消耗。
相当于 RocketMQ 除了支持通过 topic 进行一级分类,还支持通过 tag 进行二级分类。

支持事务
我们知道 Kafka 支持事务,比如生产者发三条消息 ABC,这三条消息要么同时发送成功,要么同时发送失败。

是,这确实也叫事务,但跟我们要的不太一样。
写业务代码的时候,我们更想要的事务是,"执行一些自定义逻辑"和"生产者发消息"这两件事,要么同时成功,要么同时失败。
而这正是 RocketMQ 支持的事务能力。

加入延时队列
如果我们希望消息投递出去之后,消费者不能立马消费到,而是过个一定时间后才消费,也就是所谓的延时消息,就像文章开头的定时外卖那样。如果我们使用 Kafka, 要实现类似的功能的话,就会很费劲。
但 RocketMQ 天然支持延时队列,我们可以很方便实现这一功能。
加入死信队列
消费消息是有可能失败的,失败后一般可以设置重试。如果多次重试失败,RocketMQ 会将消息放到一个专门的队列,方便我们后面单独处理。这种专门存放失败消息的队列,就是死信队列。
Kafka 原生不支持这个功能,需要我们自己实现。
消息回溯
Kafka 支持通过调整 offset 来让消费者从某个地方开始消费,而 RocketMQ,除了可以调整 offset, 还支持调整时间(kafka在0.10.1后支持调时间)
所以不那么严谨的说, RocketMQ 本质就是在架构上做了减法,在功能上做了加法的 Kafka。
这个总结是不是特别精辟。
现在大家通了吗?
最后遗留一个问题。
现在看起来,RocketMQ 好像各方面都比 Kafka 更能打。
但 Kafka 却一直没被淘汰,说明 RocketMQ 必然是有着不如 Kafka 的地方。
是啥呢?
性能,严格来说是吞吐量。
这就很奇怪了,为什么 RocketMQ 参考了 Kafka 的架构,性能却还不如 Kafka?
这个问题,我们下期聊聊。
总结
- • RocketMQ 和 Kafka 相比,在架构上做了减法,在功能上做了加法
- • 跟 Kafka 的架构相比,RocketMQ 简化了协调节点和分区以及备份模型。同时增强了消息过滤、消息回溯和事务能力,加入了延迟队列,死信队列等新特性。
RocketMQ与Kafka架构对比

1194

被折叠的 条评论
为什么被折叠?



