RocketMQ VS Kafka(二):存储架构

总述

除了上一篇描述的 namespace 和 zk 不同之外。 RocketMq 和 Kafaka 之间最大的不同就是存储架构的不同,具体来看:

针对两个 Broker, 需要保存 topic A 和 B,每个 topic 两个 partition 的场景

Kafka 如下:

RocketMq 如下:

这里最重要的区别就是:Kafka 的同步复制组是以 partition 为单位的,而 RocketMq 的复制组是以 broker 为单位的。一眼就可以看出,kafka 的写负载会更均匀,每一个 broker 都会有流量。而 RocketMq 则只能在 Leader broker 上有写流量,其他 broker 只能作为高可用的备份。

据 ali 官方的说法,RocketMq 的吞吐量为 Kafka 的 3/4 左右,可能就和这点有关吧 (具体如何测试的不清楚)。但是 RocketMq 的这种选择肯定是有自己的考虑的。猜测有如下几点:

1. Kafka 的复制组的数量会远大于 RocketMq,复制组的一致性协议显然不是免费的,而是有很大的开销。如果 partition 的数量很多,kafka 的吞吐量一定会下降

2. 当一个 broker 上线或下线后,Kafka 需要重平衡数据,而 RocketMq 在新 broker 上线时只需要做 catch up 就可以了,会比 kafka 简单很多。当然,话说回来,两种 broker 要干的活也不同,不可相提并论。

好吧,无论怎样,架构的这一差异导致 Kafka 和 RocketMq 在文件格式,读写分离,重平衡等方面的巨大不同,下面详细的说一下:

Partition VS Queue

Kafka 的 topic 被分为多个 partition,每个 partition 主要由三类文件组成:

  • log 文件,用于保存消息内容
  • index 文件,用于做消息的 hash 索引
  • timeindex 文件,用于做消息的基于消息时间戳的索引

RocketMq 的 topic 看上去被分割为多个 Queue,但这里 “分割” 是和 Kafka 有差别的。首先,消息进入 broker 后,会 append 到一个叫 commitlog 的文件中,这是 RocketMq 唯一存储消息具体内容的地方。这个基于日志的顺序写入的存储系统并不会区分 topic。在消息写入 commitlog 之后才会异步的转存至 topic 的某个 Queue 文件。Queue 类似于一个 index,其中的 item 为固定大小,并指向 commitlog 的一个 offset

Queue 中一个 item = commitLog offset (8字节) + 消息长度 (4字节) + 消息tag hashcode (8字节)

Topic 的消费端不会直接看到 commitlog,只能看到本地负责消费的 Queue,并不停的从 Queue 中拉取最新消息(拉消息具体内容时需要从 commitlog 指定的 offset 读取),并更新这个 queue 的消费进度。

所以,Kafka 的 partition 和 RocketMq 的 queue 有着本质的区别。Kafka 的 partition 是存储消息的单元,其中包含了数据,索引。而 RocketMq 的 queue 则非常简单,更像是一个为了方便消费负载均衡而存在的索引。

读写分离

Kafka 的架构决定了其写流量会比较均匀的分布到各个 broker 中。而 RocketMq 的写流量则完全集中在 leads 角色的 broker 上。以上面的 case 为例,如果 topic A,topic B 的 partition 0 和 partition 1写入流量均匀,则 Kafka 的 broker0 和 broker1 的写流量也是均匀的。而如果是 RockerMq,流量全部集中在 broker0。broker1 无流量。

消息中间件的存储,是典型的 写TPS 远远大于 读QPS 的场景。对于Kafka 来说,写流量已经做到了均匀分布了,读流量要不要分散到 follower 上意义不大。因此,kafka 的做法是读流量也必须走 leader partition,读写不分离。而对于 RocketMq,写流量集中在 broker0, 如果能将读流量分散到其他 broker 上,会起到一定的分散负载的效果。因此,即使有数据延迟的风险,RocketMq 仍然选择支持一定程度的读写分离。具体的做法是:

在 broker 拉取消息时,检查 commitlog 中当前消息写入 offset 和 消息拉取的 offset 的间隔。如果间隔大于物理内存大小 的 40%(默认),则会被认为当前 group 消费此 queue 的进度太慢了,建议从 follower 去拉取。 

详见 DefaultMessageStore#getMessage (拉取消息) 方法中:

long diff = maxOffsetPy - maxPhyOffsetPulling;
long memory = (long) (StoreUtil.TOTAL_PHYSICAL_MEMORY_SIZE * (this.messageStoreConfig.getAccessMessageInMemoryMaxRatio() / 100.0));
getResult.setSuggestPullingFromSlave(diff > memory);

这里 maxOffsetPy 为 commitlog 的当前写入 offset,maxPhyOffsetPulling 为当前本拉取的 offset.

当 suggestPullingFromSlave 为 true 时,则之后会从订阅信息中获取一个备用的 brokerId 放入 SuggestWhichBrokerId 中。 

详见 PullMessageProcessor#composeResponseHeader 方法中:

if (getMessageResult.isSuggestPullingFromSlave()) {         
responseHeader.setSuggestWhichBrokerId(subscriptionGroupConfig.getWhichBrokerWhenConsumeSlowly());
}

在客户端中,每次从 broker 拉取到消息之后,都会更新这个来自 broker 的 suggest,并会影响下一次的拉取行为。

详见 PullAPIWrapper#processPullResult

(待续)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
RocketMQKafka是两种流行的分布式消息队列系统,它们有一些区别,包括以下几个方面: 1. 架构设计:RocketMQ采用了传统的主从架构,包括Nameserver、Broker和Producer/Consumer等组件,支持多个Master-Broker和Slave-Broker。而Kafka则采用了分布式发布订阅系统的架构,包括Zookeeper、Broker和Producer/Consumer等组件,数据以分区的方式存储。 2. 消息顺序保证:RocketMQ在设计上非常注重消息的顺序性,可以保证相同Topic和相同Producer发送的消息按照发送顺序被消费。而Kafka更注重消息的分区和并行处理,可以同时处理多个消息分区,但不保证消息的全局顺序。 3. 消息持久化:RocketMQ采用了写入磁盘的方式进行消息持久化,通过CommitLog文件和ConsumeQueue文件来存储消息。而Kafka则将消息存储在磁盘上的日志文件中,通过索引来加速消息的查找。 4. 扩展性:RocketMQ支持Broker水平扩展,可以通过添加Broker节点来提高消息处理能力。而Kafka则通过增加分区和副本来实现水平扩展。 5. 社区支持:Kafka作为Apache基金会的顶级项目,有着庞大的开源社区支持,有很多成熟的生态系统和第三方工具。而RocketMQ虽然也是开源项目,但相对于Kafka社区规模较小。 需要注意的是,RocketMQKafka在一些场景下有不同的优势和适用性,选择使用哪种系统需要根据具体的业务需求来决定。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值