前言
🍊作者简介: 不肯过江东丶,一个来自二线城市的程序员,致力于用“猥琐”办法解决繁琐问题,让复杂的问题变得通俗易懂。
🍊支持作者: 点赞👍、关注💖、留言💌~
在之前的博客中,大聪明给各位小伙伴分享了一下如何在 Win10 环境下搭建 RocketMQ,相信各位小伙伴都有了属于自己的一套 RocketMQ 环境。今天咱们书接上文,大聪明继续和大家深入浅出的聊一聊 RocketMQ。
🔊 传送门:大聪明教你学Java | Win10 环境下安装部署 RocketMQ
假设现在有A、B两个服务,当 A 服务发出消息后,不想让 B 服务立马处理,而是要过半小时才让 B 服务处理。比如我们在点外卖的时候就有一个这样的场景——定时外卖,也就是说到点之后订单才会投递给商家处理。那么问题来了,这类功能该怎么实现呢?俗话说,没有什么是加一层中间层不能解决的,如果有,那就再加一层。这次我们要加的中间层是消息队 RocketMQ。
什么是 RocketMQ
RocketMQ 是一个开源的分布式消息中间件,由阿里巴巴开发,旨在提供高吞吐量、高可靠性的消息传递服务。它是一个可水平扩展的、具有低延迟和高可用性的分布式消息队列系统。目前 RocketMQ 已经是 Apache 的顶级项目,它和其它消息队列一样,接受来自生产者的消息,将消息分类,每一类是一个 topic ,消费者根据需要订阅 topic 获取里面的消息。
看到这里,各位小伙伴是不是觉得 RocketMQ,很像我们在前面提到的消息队列 Kafka 呢🧐 那么问题自然而然就来了,既然都是消息队列,RocketMQ 和 Kafka 又有什么分别呢?
RocketMQ 和 Kafka 的区别
RocketMQ 的架构其实参考了 Kafka 的设计思想,又在 Kafka 的基础上做了一些调整,这些调整用一句话总结就是,RocketMQ 和 Kafka 相比,在架构上做了减法,在功能上做了加法,下面我们来看一下这句话的含义。
在架构上做减法
首先我们先简单回顾一下 Kafka 的架构。Kafka 也是通过多个 topic 对消息进行分类。为了提升单个 topic 的并发性能,将单个 topic 拆分为多个 Partition 为了提升系统扩展性将,多个 Partition 分别部署在不同 broker 上。为了提升系统的可用性,为 Partition 加了多个副本。同时为了协调和管理 Kafka 集群的数据消息,引入 ZooKeeper 作为协调节点。如果对这些内容比较陌生,各位小伙伴可以点击下方传送门,回顾一下之前讲到的内容 👇
🔊 传送门:大聪明教你学Java | 深入浅出聊 Kafka
下面我们再来一起看看 RocketMQ 在架构上做了哪些调整。
简化协调节点
ZooKeeper 在 Kafka 架构中会和 Broker 通信,来维护 Kafka 集群信息。一个新的 broker 连上 ZooKeeper 后,其他 broker 就能立马感知到他的加入。像这种能在分布式环境下,让多个实例同时获取到同一份信息的服务,是所谓的分布式协调服务。ZooKeeper 作为一个通用的协调服务,它不仅可以用于服务注册和发现,还可以用于分布式锁、配置管理等场景。但是话说回来,Kafka 其实只用到了他的部分功能,就多少有点杀鸡用牛刀的味道了。所以 RocketMQ 直接将 ZooKeeper 去掉,换成了 Nameserver,用一种更轻量级的方式管理消息队列的集群信息。当然,开发 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 的底层存储中, Partition 分区其实在底层有很多段,也就是 segment 组成,每个 segment 可以认为就是个小文件,将消息数据写入到 Partition 分区,本质上就是将数据写入到某个 segment 的文件下。我们知道磁盘顺序写的性能会比随机写快很多,差距高达几十倍。那么为了提升性能,Kafka 对每个小文件都是顺序写,如果只有一个 segment 文件,那写文件的性能会很好。但当 topic 变多之后, topic 底下的 Partition 分区也会变多,对应 Partition 底下的 segment 文件也会变多。同时写多个 topic ,底下的 Partition 就是同时写多个文件。虽然每个文件内部都是顺序写,但多个文件存放在磁盘的不同地方,原本顺序写磁盘就可能劣化变成了随机写,于是写性能就降低了。
而在 RocketMQ 的底层存储中,为了缓解同时写多个文件带来的随机写问题,RocketMQ 索性将单个broke底下的多个 topic 数据全部写到一个逻辑文件 CommitLog 上,这就消除了随机写多文件的问题。将所有写操作都变成了顺序写,大大提升了 RocketMQ 在多 topic 场景下的写性能。
简化备份模型
我们知道 Kafka 会将 Partition 分散到多个 broker 中,并为 Partition 配置副本,将爬梯性分为 leader 和 follower,也就是主和从。主从 Partition 之间会建立数据同步,本质上就是同步 Partition 底下的 segment 的文件数据。而 RocketMQ 则是将 broker 上的所有 topic 数据写到 CommitLog 上,如果还像 Kafka 那样给每个分区单独建立同步通信,就还得将 CommitLog 里的内容拆开,这就还是退化为随机读写了。于是 RocketMQ 索性直接同步 CommitLog 文件,以 broker 为单位区分主从,保持高可用的同时也大大简化了备份模型。到这里,我们所熟知的 Kafka 架构就变成了 RocketMQ 架构 👇
在功能上做加法
虽然 RocketMQ 的架构比 Kafka 简单,但功能却比 Kafka 要更丰富。
消息过滤
我们知道 Kafka 支持通过 topic 将数据进行分类,比如订单数据和用户数据是两个不同的topic,但如果我们还想再进一步分类呢?比如同样是用户数据,还能根据 VIP 等级进一步分类。假设我们只需要获取 VIP5 的用户数据,在 Kafka 里,消费者需要消费 topic 为用户数据的所有消息,再将 VIP5 的用户数据过滤出来。而 RocketMQ 支持对消息打上标记,也就是打tag,消费者能根据 tag 过滤所需要的数据,这样消费者就可以只获取这部分数据,从而省下了消费者过滤数据时的资源消耗。
支持事务
我们知道 Kafka 支持事务,比如生产者发三条消息,这三条消息要么同时发送成功,要么同时发送失败。这确实也叫事务,但跟我们要的不太一样。写业务代码的时候,我们更想要的事务是执行一些自定义逻辑和生产者发消息,这两件事要么同时成功,要么同时失败,而这正是 RocketMQ 支持的事务能力。
加入延时队列
如果我们希望消息被投递出去之后,消费者不能立马消费到,而是过一定时间后才消费,也就是所谓的延时消息。这可以用 RocketMQ 的延时队列实现,而 Kafka 就得让程序员自己实现类似的功能了。
加入死信队列
消费消息是有可能失败的,失败后一般可以设置重试,如果多次重试失败,RocketMQ 会将消息放到一个专门的队列,方便我们后面单独处理,这种专门存放失败消息的队列就是死信队列。而 Kafka 原生并不支持死信队列,这个功能就需要我们自己实现。
消息回溯
Kafka 支持通过调整 offset 让消费者从某个地方开始消费。而 RocketMQ 除了可以调整 offset ,还支持调整时间。当然了,开发 Kafka 大佬们也意识了到了这个问题,所以从 Kafka 0.10.1 版本后也支持调整时间。
所以不那么严谨的说,RocketMQ 本质就是在架构上做了减法,在功能上做了加法的 Kafka 😎
小结
本人经验有限,有些地方可能讲的没有特别到位,如果您在阅读的时候想到了什么问题,欢迎在评论区留言,我们后续再一一探讨🙇
希望各位小伙伴动动自己可爱的小手,来一波点赞+关注 (✿◡‿◡) 让更多小伙伴看到这篇文章~ 蟹蟹呦(●’◡’●)
如果文章中有错误,欢迎大家留言指正;若您有更好、更独到的理解,欢迎您在留言区留下您的宝贵想法。
你在被打击时,记起你的珍贵,抵抗恶意;
你在迷茫时,坚信你的珍贵,抛开蜚语;
爱你所爱 行你所行 听从你心 无问东西