(RocketMQ)高性能原理

172 阅读4分钟

消息持久化

RocketMQ采用磁盘文件保存,存储文件主要由三部分组成:

1.CommitLog

所有消息都会顺序写入CommitLog,大小为1G,超过大小则新建一个文件,文件名为第一条消息的偏移量。这样的好处是可以减少查找目标文件的时间,让消息以最快的速度落盘。对比Kafka存文件时,需要寻找消息所属Partition文件,再完成写入,当Topic比较多时,这样的Partition寻址就会浪费比较多的时间,所以Kafka不太适合多Topic的场景。而RocketMQ的这种快速落盘的方式在多Topic场景下,优势就比较明显。

2.ConsumerQueue

ConsumeQueue文件主要是加速消费者的消息索引。他的每个文件夹对应RocketMQ中的一个MessageQueue,文件夹下的文件记录了每个MessageQueue中的消息在CommitLog文件当中的偏移量。这样,消费者通过ComsumeQueue文件,就可以快速找到CommitLog文件中感兴趣的消息记录。而消费者在ConsumeQueue文件当中的消费进度,会保存在 config/consumerOffset.json文件当中。

3.IndexFile

IndexFile文件主要是辅助消息检索。消费者进行消息消费时,通过ConsumeQueue文件就足够完成消息检索了,但是如果要按照MeessageId或者MessageKey来检索文件,比如RocketMQ管理控制台的消息轨迹功能,ConsumeQueue文件就不够用了。IndexFile文件就是用来辅助这类消息检索的。他的文件名比较特殊,不是以消息偏移量命名,而是用的时间命名。但是其实,他也是一个固定大小的文件。

读写队列

在RocketMQ的管理控制台创建Topic时,可以看到要单独设置读队列和写队列。通常在运行时,都需要设置读队列=写队列。写队列控制消息的写入和存储,读队列负责消息读取同时记录消费的偏移量。

写队列>读队列

在往写队列里写Message时,会同步写入到一个对应的读队列中。如果写队列>读队列,例如:4个写队列,3个读队列。则会造成有一个队列的没有人消费,消息就会出现丢失的情况。

读队列>写队列

例如:3个写队列,4个读队列。则会有一个读队列持续在空转带来性能的损耗。

主从复制

如果Broker已集群方式部署,,会有一个master节点和多个slave节点,消息需要从Master复制到Slave上。而消息复制的方式分为同步复制和异步复制。

同步复制

同步复制是等master和slave都写入才会返回成功状态。在这种情况下出现了master宕机,salve上有全部的数据信息。但是同步复制会带来数据写入延迟降低服务的吞吐量。

异步复制

异步复制是只要master写入成功就会返回成功状态,然后在异步将消息写入salve节点。这种情况下系统拥有较低的延迟和较高的吞吐量,但是出现master宕机,但是有些数据没有写入完会有数据丢失的可能性。

过期文件删除

如何判断过期文件

RocketMQ中,CommitLog文件和ConsumeQueue文件都是以偏移量命名,对于非当前写的文件,如果超过了一定的保留时间,那么这些文件都会被认为是过期文件,随时可以删除。这个保留时间就是在broker.conf中配置的fileReservedTime属性。

何时删除过期文件

RocketMQ内部有一个定时任务,对文件进行扫描,并且触发文件删除的操作。用户可以指定文件删除操作的执行时间。在broker.conf中deleteWhen属性指定。默认是凌晨四点。

负载均衡

Producer负载均衡

Producer发送消息时,默认会轮询目标Topic下的所有MessageQueue,并采用递增取模的方式往不同的MessageQueue上发送消息,以达到让消息平均落在不同的queue上的目的。而由于MessageQueue是分布在不同的Broker上的,所以消息也会发送到不同的broker上

Consumer负载均衡

  • 集群模式:在集群消费模式下,每条消息只需要投递到订阅这个topic的Consumer Group下的一个实例即可。RocketMQ采用主动拉取的方式拉取并消费消息,在拉取的时候需要明确指定拉取哪一条message queue。而每当实例的数量有变更,都会触发一次所有实例的负载均衡,这时候会按照queue的数量和实例的数量平均分配queue给每个实例。
  • 广播模式:广播模式下,每一条消息都会投递给订阅了Topic的所有消费者实例,所以也就没有消息分配这一说。而在实现上,就是在Consumer分配Queue时,所有Consumer都分到所有的Queue。