RocketMQ基础以及和 Kafka 有什么区别

RocketMQ 是阿里自研的国产消息队列,目前已经是 Apache 的顶级项目。和其他消息队列一样,它接受来自生产者的消息,将消息分类,每一类是一个 topic,消费者根据需要订阅 topic,获取里面的消息。

RocketMQ 和 Kafka 的区别

RocketMQ 的架构其实参考了 Kafka 的设计思想,同时又在 Kafka 的基础上做了一些调整。

这些调整,用一句话总结就是,"和 Kafka 相比,RocketMQ 在架构上做了减法,在功能上做了加法"。

在架构上做减法

我们来简单回顾下消息队列 Kafka 的架构。kakfa 也是通过多个 topic 对消息进行分类。

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

为了提升系统扩展性,将多个 partition 分别部署在不同 broker 上。

为了提升系统的可用性,为 partition 加了多个副本。

为了协调和管理 Kafka 集群的数据信息,引入Zookeeper作为协调节点。

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。这个总结是不是特别精辟。现在大家通了吗?

性能差于kafka

最后遗留一个问题。现在看起来,RocketMQ 好像各方面都比 Kafka 更能打。但 Kafka 却一直没被淘汰,说明 RocketMQ 必然是有着不如 Kafka 的地方。是啥呢?性能,严格来说是吞吐量。

阿里中间件团队对它们做过压测,同样条件下,kafka 比 RocketMQ 快 50%左右。但即使这样,RocketMQ 依然能每秒处理 10w 量级的数据,依旧非常能打。你不能说 RocketMQ 弱,只能说 Kafka 性能太强了。不过这就很奇怪了,为什么 RocketMQ 参考了 kafka 的架构,却不能跟 kafka 保持一样的性能呢?

零拷贝sendfile 与 mmap

kafka 为什么性能比 RocketMQ 好。这是因为 RocketMQ 使用的是 mmap 零拷贝技术,而 kafka 使用的是 sendfile。kafka 以更少的拷贝次数以及系统内核切换次数,获得了更高的性能。但问题又来了,为什么 RocketMQ 不使用 sendfile?参考 kafka 抄个作业也不难啊?我们来看下 sendfile 函数长啥样。

bash 复制代码
 ssize_t sendfile(int out_fd, int in_fd, off_t* offset, size_t count);
 // num = sendfile(xxx);

再来看下 mmap 函数长啥样。

bash 复制代码
void *mmap(void *addr, size_t length, int prot, int flags,
           int fd, off_t offset);
// buf = mmap(xxx)

注释里写的是两个函数的用法。

mmap 返回的是数据的具体内容,应用层能获取到消息内容并进行一些逻辑处理。

而 sendfile 返回的则是发送成功了几个字节数,具体发了什么内容,应用层根本不知道。

而 RocketMQ 的一些功能,却需要了解具体这个消息内容,方便二次投递等,比如将消费失败的消息重新投递到死信队列中,如果 RocketMQ 使用 sendfile,那根本没机会获取到消息内容长什么样子,也就没办法实现一些好用的功能了。

而 kafka 却没有这些功能特性,追求极致性能,正好可以使用 sendfile。

除了零拷贝以外,kafka 高性能的原因还有很多,比如什么批处理,数据压缩啥的,但那些优化手段 rocketMQ 也都能借鉴一波,唯独这个零拷贝,那是毫无办法。

所以还是那句话,没有一种架构是完美的,一种架构往往用于适配某些场景,你很难做到既要又要还要。当场景不同,我们就需要做一些定制化改造,通过牺牲一部分能力去换取另一部分能力。做架构,做到最后都是在做折中。是不是感觉升华了。kafka 和 RocketMQ 怎么选?这时候大家估计还是想知道 kafka 和 RocketMQ 到底该怎么选,用哪个。官方点的回答是"这个要看场景的"。说了等于没说。这不是我的风格。我的标准只有一个,如果是大数据场景,比如你能频繁听到 spark,flink 这些关键词的时候,那就用 kafka。除此之外,如果公司组件支持,尽量用 RocketMQ。

ps Rocketmq 社区不如kafka,除了Java用户,其他语言的sdk 万年不更新,一堆bug也不修

RocketMQ的架构

假设田螺哥开了一家全球快递公司(类比 RocketMQ),负责处理海量包裹(消息)的收发。

RocketMQ四大核心角色

1.1 NameServer(总通讯录)

可以把它比喻作:一本全球邮局地址簿,记录所有邮局(Broker)的位置和状态。

功能:

寄件人(Producer)和收件人(Consumer)每次寄/收快递前,先查这本通讯录,找到最近的邮局地址。

无状态设计:通讯录本身不存快递,只记录地址,即使某页撕掉了(NameServer 宕机),其他页还能用,系统照常运行。

1.2 Broker(邮局+仓库)

可以把它比喻作:遍布全球的邮局+仓库综合体,负责收快递、存快递、送快递。

内部结构:

局长(Master):处理寄件请求(写操作),所有快递按顺序堆进大仓库(CommitLog)。

副局长(Slave):备份局长的仓库数据,平时帮忙送快递(读操作),局长病倒了(宕机)时代班。

分拣系统:根据快递目的地(Topic),把快递分到不同的邮箱(队列)里(如"北京邮箱""上海邮箱")。

1.3 Producer(寄件人)

可以把它比喻作:淘宝商家,每天要发几万件快递(消息)。

流程:

查通讯录(NameServer)→ 找到处理"北京快递"的邮局(Broker)。

把快递贴上"北京-朝阳区-1号邮箱"(Topic-QueueId)标签,交给邮局。

1.4 Consumer(收件人)

可以把它比喻作:朝阳区的居民们(消费者组),每天收快递。

流程:

查通讯录(NameServer)→ 找到"北京-朝阳区"对应的邮局(Broker)。

居民们分工:张三收1号邮箱,李四收2号邮箱......(队列负载均衡)。

支持两种模式:

拼团收件(集群模式):一个快递只由一个人签收(避免重复消费)。

广播通知(广播模式):所有居民都收到同一份快递(如小区停电通知)。

2. RocketMQ的收发消息(附比喻:快递流转全流程)

2.1 寄快递(消息发送)

2.2 存快递(消息存储)

大仓库(CommitLog):所有快递按到达顺序堆成一座山,记录流水账(顺序写入磁盘,性能极高)。

分拣标签(ConsumerQueue):每个邮箱(队列)对应一个索引本,记录"北京-1号邮箱的快递在大仓库第100-200号位置"。

2.3 收快递(消息消费)

3. RocketMQ的高可用设计(邮局备份)

主从热备(局长+副局长)

平时:局长(Master)收快递,副局长(Slave)默默抄写局长的仓库记录(同步数据)。

局长生病:副局长顶上,变身新局长(需人工切换或工具自动切换),保证快递不收影响。

多邮局协作(分布式集群):

4. 水平扩展(增加邮局和邮箱)

如果出现这样的问题:北京快递量暴增,一个邮局处理不过来!

解决方案:

开分店:在北京多开几家邮局(Broker 扩容)。

增加邮箱:把"北京快递"拆成"海淀区邮箱""朝阳区邮箱"......(Topic 分多个队列)。

寄件分流:商家轮流往不同邮箱投递(Producer 轮询发消息)。

收件分工:海淀居民收海淀邮箱,朝阳居民收朝阳邮箱(Consumer 负载均衡)

5. RocketMQ的优点

通讯录轻量(NameServer 无状态):地址簿不复杂,容易维护。

仓库流水账(CommitLog 顺序写):快递堆得快,找得也快。

分邮箱管理(Topic-Queue 队列):分工明确,效率翻倍。

主从备份(Broker 主从):局长倒下了,副局长立刻顶上。

弹性扩容:快递再多也不怕,多开邮局多分箱!

相关推荐
AutoMQ3 小时前
AutoMQ x Lightstreamer: Kafka 金融数据实时分发新方案
分布式·金融·kafka
Code88483 小时前
[从青铜到王者] Spring Boot+Redis+Kafka电商场景面试全解析
spring boot·redis·微服务·kafka·java面试·电商架构
七夜zippoe4 小时前
JVM 调优在分布式场景下的特殊策略:从集群 GC 分析到 OOM 排查实战(二)
jvm·分布式
一个帅气昵称啊5 小时前
在.NET中实现RabbitMQ客户端的优雅生命周期管理及二次封装
分布式·后端·架构·c#·rabbitmq·.net
王百万_5 小时前
【浅谈Spark和Flink区别及应用】
大数据·数据库·分布式·flink·spark·数据治理·数据库架构
BullSmall6 小时前
Kafka 图形界面客户端工具
笔记·kafka
励志成为糕手6 小时前
Kafka事务:构建可靠的分布式消息处理系统
分布式·kafka·消息队列·linq·数据一致性
一條狗6 小时前
学习日报 20250921|MQ (Kafka)面试深度复盘
java·中间件·kafka
weixin_436525076 小时前
windows-安装kafka并启动
分布式·kafka