☞ RocketMQ 是什么

官方点说,RocketMQ 是一个统一的消息引擎、一个轻量级的数据处理平台

通俗点说,RocketMQ 是一个 队列模型 的消息中间件,具有高性能、高可靠、高实时、分布式 的特点

☞ RocketMQ 的特性

☞ RocketMQ 的模型

主题模型

在主题模型中,消息的生产者称为 发布者(Publisher) ,消息的消费者称为 订阅者(Subscriber) ,存放消息的容器称为 主题(Topic) ,发布者将消息发送到指定主题中,订阅者需要 提前订阅主题 才能接受特定主题的消息,通常的主题模型如下图:

RocketMQ 的主题模型

生产者组中的生产者会向主题发送消息,而主题中存在多个队列,生产者每次生产消息之后是指定主题中的某个队列发送消息

每个主题中都有多个队列,集群消费模式下,一个消费者集群多台机器共同消费一个 Topic 的多个队列,一个队列只会被一个消费者消费。如果某个消费者挂掉,分组内其它消费者会接替挂掉的消费者继续消费。就像上图中 Consumer1 和 Consumer2 分别对应着两个队列,而 Consuer3 是没有队列对应的,所以一般来讲要控制消费者组中的消费者个数和主题中队列个数相同 ,当然也可以消费者个数小于队列个数,只不过不太建议

每个消费组在每个队列上都需要维护一个消费位置(offset)

发布订阅模式中一般会涉及到多个消费者组,而每个消费者组在每个队列中的消费位置都是不同的,同一个消息被 A 消费组消费完后还会被 B 消费组消费,所以消息是不会删除的,仅仅是为每个消费者组维护一个 消费位移(offset) ,每次消费者组消费完以后队列把维护的消费位移 +1,这样就不会出现消息重复消费

为什么一个主题中需要维护多个队列

提高并发能力。按道理来说每个主题中只存在一个队列也是可行的,如果每个主题中只存在一个队列,这个队列中也维护着每个消费者组的消费位置,其实也可以做到发布订阅模式 ,但生产者只能向一个队列发送消息,一个消费者组中只能有一个消费者在消费,并发能力很小

所以 RocketMQ 通过使用在一个 Topic 中配置多个队列并且每个队列维护每个消费者组的消费位置实现了发布订阅模式

☞ RocketMQ 的架构

四大角色:Broker 、NameServer 、Producer 、Consumer

四者关系图如下

Broker 是需要保证高可用的,如果整个系统仅仅靠着一个 Broker 来维持的话,那么这个 Broker 的压力会很大,所以我们需要使用多个 Broker 来保证 负载均衡

如果说,我们的消费者和生产者直接和多个 Broker 相连,那么当 Broker 修改的时候必定会牵连着每个生产者和消费者,这样就会产生耦合问题,而 NameServer 注册中心就是用来解决这个问题的

☞ RocketMQ 的存储机制

队列是以什么样的形式存在的?队列中的消息又是如何进行存储持久化的?

RocketMQ 消息存储架构

整个消息存储的结构,最主要的就是 CommitLog 和 ConsumeQueue ,而 ConsumeQueue 可以大概理解为 Topic 中的队列

RocketMQ 采用的是 混合型的存储结构 ,即为 Broker 单个实例下所有的队列共用一个日志数据文件来存储消息,而同样高并发的 Kafka 中会为每个 Topic 分配一个存储文件

RocketMQ 这么做的原因是 提高数据的写入效率 ,不分 Topic 意味着有更大的几率获取 成批 的消息进行数据写入,但是读取消息的时候需要遍历整个大文件,非常耗时

所以在 RocketMQ 中又使用了 ConsumeQueue 作为每个队列的索引文件来 提升读取消息的效率。可以直接根据队列的消息序号,计算出索引的全局位置(索引序号 × 索引固定⻓度20),然后直接读取这条索引,再根据索引中记录的消息的全局位置,找到消息

大致的流程入下图所示:

  1. 生产者发送消息会指定 Topic、QueueId 和消息内容,Broker 收到之后直接 全部顺序存储到了 CommitLog
  2. Broker 根据生产者指定的 Topic 和 QueueId 将这条消息本身在 CommitLog 的 offset、消息大小和 Tag 的 HASH 值存入对应的 ConsumeQueue 索引文件中
  3. 每个队列中都保存了 ConsumeOffset(每个消费者组的消费位置),消费者拉取消息进行消费的时候只需要根据 ConsumeOffset 获取下一个未被消费的消息即可

☞ RocketMQ 的部署

部署特点

集群工作方式

💐 鸣谢

Apache RocketMQ 👍

消息队列扫盲 👍