记录自己学习RocketMQ的学习历程,一切都从基础玩起,了解一下RocketMQ 核心概念。
RocketMQ基础知识
RocketMQ 部署架构
在 RocketMQ 主要的组件如下:
1、Nameserver
Nameserver 集群, topic 的路由注册中心, 为客户端根据 Topic 提供路由服务, 从而引导客户端向 Broker 发送消息。 Nameserver 之间的节点不通信。 路由信息在Nameserver 集群中数据一致性采取的最终一致性。
Nameserver中Topic的路由信息存储在内存中,持久化的Topic路由信息在Broker中${ROCKETMQ_HOME}/store/config/topics.json里边。
2、Broker
消息存储服务器,分为两种角色: Master与Slave,上图中呈现的就是2主2从的部署架构, 在RocketMQ中,主服务承担读写操作,从服务器作为一个备份,当主服务器存在压力时,从服务器可以承担读服务( 消息消费)。所有 Broker,包含 Slave 服务器每隔30s 会向 Nameserver 发送心跳包, 心跳包中会包含存在在Broker上所有的 topic 的路由信息。
在 RocketMQ4.5.0 版本后引入了多副本机制, 即一个复制组(master-slave)可以演变为基于 raft 协议的复制组, 复制组内部使用 raft 协议保证 broker 节点数据的强一致性, 该部署架构在金融行业用的比较多。
3、Client
消息客户端, 包括消息生产者和消费消费者。客户端在同一时间只会连接一台 nameserver, 只有在连接出现异常时才会向尝试连接另外一台。 客户端每隔 30s 向 Nameserver 发起 topic 的路由信息查询。
消息订阅模型
在 RocketMQ 的消息消费模式采用的是发布与订阅模式。topic
: 一类消息的集合, 消息发送者将一类消息发送到一个主题中, 例如订单模块将订单发送到order_topic中,而用户登录时,将登录事件发送到user_login_topic中。consumegroup
: 消息消费组, 一个消费单位的“ 群体” ,消费组首先在启动时需要订阅需要消费的topic。 一个topic可以被多个消费组订阅, 同样一个消费组也可以订阅多个主题。 一个消费组拥有多个消费者。
消费模式
广播模式
一个消费组内的所有消费者每一个都会处理topic中的每一条消息, 通常用于刷新内存缓存。
集群模式
一个消费组内的所有消费者共同消费一个 topic 中的消息, 即分工协作, 一个消费者消费一部分数据, 启动负载均衡,集群模式是非常普遍的模式, 符合分布式架构的基本理念, 即横向扩容, 当前消费者如果无法快速及时处理消息时,可以通过增加消费者的个数横向扩容, 快速提高消费能力, 及时处理挤压的消息。
那集群模式下, 消费者是如何来分配消息的呢?
例如上面实例中 order_topic 有 16 个队列, 那一个拥有 3 个消费者的消费组如何来分配队列中。
在 MQ 领域有一个不成文的约定: 同一个消费者同一时间可以分配多个队列, 但一个队列同一时间只会分配给一个消费者。
RocketMQ 提供了众多的队列负载算法, 其中最常用的两种平均分配算法。
AllocateMessageQueueAveragely(平均分配)
其算法的特点是用总数除以消费者个数, 余数按消费者顺序分配给消费者。
为了说明这两种分配算法的分配规则, 现在对 16 个队列, 进行编号, 用 q0~q15 表示,
消费者用 c0~ c2 表示。
AllocateMessageQueueAveragely 分配算法的队列负载机制如下:
c0: q0 q1 q2 q3 q4 q5
c1: q6 q7 q8 q9 q10
c2: q11 q12 q13 q14 q15
AllocateMessageQueueAveragelyByCircle(轮流平均分配)
该分配算法的特点就是轮流一个一个分配。
c0: q0 q3 q6 q9 q12 q15
c1: q1 q4 q7 q10 q13
c2: q2 q5 q8 q11 q14
温馨提示
: 如果 topic 的队列个数小于消费者的个数, 那有些消费者无法分配到消息。在 RocketMQ 中一个 topic 的队列数直接决定了最大消费者的个数, 但 topic 队列个数的增加对 RocketMQ 的性能不会产生影响。
在实际过程中, 对主题进行扩容(增加队列个数)或者对消费者进行扩容、 缩容是一件非常寻常的事情, 那如果新增一个消费者, 该消费者消费哪些队列呢? 这就涉及到消息消费队列的重新分配, 即消费队列重平衡机制。
在 RocketMQ 客户端中会每隔 20s 去查询当前 topic 的所有队列、 消费者的个数, 运用队列负载算法进行重新分配, 然后与上一次的分配结果进行对比, 如果发生了变化, 则进行队列重新分配; 如果没有发生变化, 则忽略。
上述整个过程无需应用程序干预, 由 RocketMQ 完成。 大概的做法就是将将原先分配给自己但这次不属于的队列进行丢弃, 新分配的队列则创建新的拉取任务。