一、消息的消费
消费者从 Broker 中获取消息的方式有两种:pull 拉取方式和 push 推动方式。
消费者组对于消息消费的模式又分为两种:集群消费 Clustering 和广播消费 Broadcasting。
二、获取消费类型
1、拉取式消费
Consumer 主动从 Broker 中拉取消息,主动权由 Consumer 控制。一旦获取了批量消息,就会启动消费过程。不过,该方式的实时性较弱,即 Broker 中有了新的消息时消费者并不能及时发现并消费。
由于拉取时间间隔是由用户指定的,所以在设置该间隔时需要注意平稳:间隔太短,空请求比例会增加;间隔太长,消息的实时性太差
2、推送式消费
该模式下 Broker 收到数据后会主动推送给 Consumer。该获取方式一般实时性较高。
该获取方式是典型的发布-订阅模式,即 Consumer 向其关联的 Queue 注册了监听器,一旦发现有新的消息到来就会触发回调的执行,回调方法是 Consumer 去 Queue 中拉取消息。而这些都是基于 Consumer 与
Broker 间的长连接的。长连接的维护是需要消耗系统资源的。
3、对比
- pull:需要应用去实现对关联 Queue 的遍历,实时性差;但便于应用控制消息的拉取
- push:封装了对关联 Queue 的遍历,实时性强,但会占用较多的系统资源
三、消费模式
1、广播消费
广播消费模式下,相同 Consumer Group 的每个 Consumer 实例都接收同一个 Topic 的全量消息。即每条消息都会被发送到 Consumer Group 中的每个Consumer。
2、集群消费
集群消费模式下,相同 Consumer Group 的每个 Consumer 实例平均分摊同一个 Topic 的消息。即每条消息只会被发送到 Consumer Group 中的某个Consumer。
3、消息进度保存
- 广播模式:消费进度保存在 consumer 端。因为广播模式下 consumer group 中每个 consumer 都会消费所有消息,但它们的消费进度是不同。所以 consumer 各自保存各自的消费进度。
- 集群模式:消费进度保存在 broker 中。consumer group 中的所有 consumer 共同消费同一个 Topic 中的消息,同一条消息只会被消费一次。消费进度会参与到了消费的负载均衡中,故消费进度是需要共享的。下图是
broker 中存放的各个 Topic 的各个 Queue 的消费进度。
四、Rebalance 机制
Rebalance 机制讨论的前提是:集群消费。
1、什么是 Rebalance
Rebalance 即再均衡,指的是,将⼀个 Topic 下的多个 Queue 在同⼀个 Consumer Group 中的多个 Consumer 间进行重新分配的过程。
Rebalance 机制的本意是为了提升消息的并行消费能力。例如,⼀个 Topic 下 5 个队列,在只有 1 个消费者的情况下,这个消费者将负责消费这 5 个队列的消息。如果此时我们增加⼀个消费者,那么就可以给其中⼀个消费者分配
2 个队列,给另⼀个分配 3 个队列,从而提升消息的并行消费能力。
2、Rebalance 限制
由于⼀个队列最多分配给⼀个消费者,因此当某个消费者组下的消费者实例数量大于队列的数量时,多余的消费者实例将分配不到任何队列。
3、Rebalance 危害
Rebalance 的在提升消费能力的同时,也带来一些问题:
消费暂停:在只有一个 Consumer 时,其负责消费所有队列;在新增了一个 Consumer 后会触发 Rebalance 的发生。此时原 Consumer 就需要暂停部分队列的消费,等到这些队列分配给新的 Consumer
后,这些暂停消费的队列才能继续被消费。
消费重复:Consumer 在消费新分配给自己的队列时,必须接着之前 Consumer 提交的消费进度的 offset 继续消费。然而默认情况下,offset 是异步提交的,这个异步性导致提交到 Broker 的 offset 与
Consumer 实际消费的消息并不一致。这个不一致的差值就是可能会重复消费的消息。
同步提交:consumer 提交了其消费完毕的一批消息的 offset 给 broker 后,需要等待 broker 的成功 ACK。当收到 ACK 后,consumer 才会继续获取并消费下一批消息。在等待 ACK 期间,consumer 是阻塞的。
异步提交:consumer 提交了其消费完毕的一批消息的 offset 给 broker 后,不需要等待 broker 的成 功 ACK。consumer 可以直接获取并消费下一批消息。
对于一次性读取消息的数量,需要根据具体业务场景选择一个相对均衡的是很有必要的。因为数量过大,系统性能提升了,但产生重复消费的消息数量可能会增加;数量过小,系统性能会下降,但被重复消费的消息数量可能会减少。
消费突刺:由于 Rebalance 可能导致重复消费,如果需要重复消费的消息过多,或者因为 Rebalance 暂停时间过长从而导致积压了部分消息。那么有可能会导致在 Rebalance 结束之后瞬间需要消费很多消息。
4、Rebalance 产生的原因
导致 Rebalance 产生的原因,无非就两个:消费者所订阅 Topic 的 Queue 数量发生变化,或消费者组中消费者的数量发生变化。
(1)Queue 数量发生变化的场景:
- Broker 扩容或缩容
- Broker 升级运维
- Broker 与 NameServer 间的网络异常
- Queue 扩容或缩容
(2)消费者数量发生变化的场景:
- Consumer Group 扩容或缩容
- Consumer 升级运维
- Consumer 与 NameServer 间网络异常
5、Rebalance 过程
在 Broker 中维护着多个 Map 集合,这些集合中动态存放着当前 Topic 中 Queue 的信息、Consumer Group 中 Consumer 实例的信息。一旦发现消费者所订阅的 Queue
数量发生变化,或消费者组中消费者的数量发生变化,立即向 Consumer Group 中的每个实例发出 Rebalance 通知。
TopicConfigManager:key 是 topic 名称,value 是 TopicConfig。TopicConfig 中维护着该 Topic 中所有 Queue 的数据。
ConsumerManager:key 是 Consumser Group Id,value 是 ConsumerGroupInfo。
ConsumerGroupInfo 中维护着该 Group 中所有 Consumer 实例数据。ConsumerOffsetManager:key 为Topic 与订阅该 Topic 的 Group 的组合,即 topic@group, value 是一个内层 Map。内层 Map 的 key 为 QueueId,内层 Map 的 value 为该 Queue 的消费进度 offset。
Consumer 实例在接收到通知后会采用Queue 分配算法自己获取到相应的 Queue,即由 Consumer 实例自主进行 Rebalance。
6、与 Kafka 对比
在 Kafka 中,一旦发现出现了 Rebalance 条件,Broker 会调用 Group Coordinator 来完成 Rebalance。 Coordinator 是 Broker 中的一个进程。Coordinator 会在
Consumer Group 中选出一个 Group Leader。由这个 Leader 根据自己本身组情况完成 Partition 分区的再分配。这个再分配结果会上报给 Coordinator,并由 Coordinator 同步给
Group 中的所有 Consumer 实例。
Kafka 中的 Rebalance 是由 Consumer Leader 完成的。而 RocketMQ 中的 Rebalance 是由每个 Consumer 自身完成的,Group 中不存在 Leader。
五、Queue 分配算法
一个 Topic 中的 Queue 只能由 Consumer Group 中的一个 Consumer 进行消费,而一个 Consumer 可以同时消费多个 Queue 中的消息。那么 Queue 与 Consumer
间的配对关系是如何确定的,即 Queue 要分配给哪个 Consumer 进行消费,也是有算法策略的。常见的有四种策略。这些策略是通过在创建 Consumer 时的构造器传进去的。
1、平均分配策略
该算法是要根据 avg = QueueCount / ConsumerCount
的计算结果进行分配的。如果能够整除,则按顺序将 avg 个 Queue 逐个分配 Consumer;如果不能整除,则将多余出的 Queue 按照
Consumer 顺序逐个分配。
该算法即,先计算好每个 Consumer 应该分得几个 Queue,然后再依次将这些数量的 Queue 逐个分配个 Consumer。
2、环形平均策略
环形平均算法是指,根据消费者的顺序,依次在由 queue 队列组成的环形图中逐个分配。
该算法不用事先计算每个 Consumer 需要分配几个 Queue,直接一个一个分即可。
3、一致性 hash 策略
该算法会将 consumer 的 hash 值作为 Node 节点存放到 hash 环上,然后将 queue 的 hash 值也放到 hash 环上,通过顺时针方向,距离 queue 最近的那个 consumer 就是该 queue
要分配的 consumer。
该算法存在的问题:分配不均。
4、同机房策略
该算法会根据 queue 的部署机房位置和 consumer 的位置,过滤出当前 consumer 相同机房的 queue。然后按照平均分配策略或环形平均策略对同机房 queue 进行分配。如果没有同机房
queue,则按照平均分配策略或环形平均策略对所有 queue 进行分配。
5、对比
一致性 hash 算法存在的问题: 两种平均分配策略的分配效率较高,一致性 hash 策略的较低。因为一致性 hash 算法较复杂。另外,一致性 hash 策略分配的结果也很大可能上存在不平均的情况。
一致性 hash 算法存在的意义: 其可以有效减少由于消费者组扩容或缩容所带来的大量的 Rebalance。
一致性 hash 算法的应用场景:Consumer 数量变化较频繁的场景。
六、至少一次原则
RocketMQ 有一个原则:每条消息必须要被成功消费一次。
那么什么是成功消费呢?Consumer 在消费完消息后会向其消费进度记录器提交其消费消息的 offset, offset 被成功记录到记录器中,那么这条消费就被成功消费了。
什么是消费进度记录器?
对于广播消费模式来说,Consumer 本身就是消费进度记录器。
对于集群消费模式来说,Broker 是消费进度记录器。