接收消息与ACK机制
在知道了发送者
的发送和存储机制之后,就可以来看看消费者
的接收和消费机制了。
消费者组
组(Group)
是对Topic
概念的进一步划分,类似于在大主题中再区分出不同的子主题。

当消息从Producer生产者
经过Broker
发出后,在正常情况下,关注同一个TopicA
的两个消费者组stock_consume_group
和markting_consume_group
都可以接收到这条消息,且在正常情况下,只有其中一台机器能够接收到消息。
也就是说,不管有多少个子系统,每个系统都能够接收到相同Topic
发出的消息。但每个子系统不管有多少台机器,始终只有一台机器能够接收到消息。
但如果希望子系统中的每台机器
都能够接收到这条消息,那么就需要知道集群模式
和广播模式
的区别了。
集群模式
,这是RocketMQ默认消息消费模式,它会把收到的消息只转交给组内的某一台机器处理。广播模式
则刚好相反,它会让组内的所有机器都来处理同一条消息。
RocketMQ将一个Topic
的多个MessageQueue
分布在多个Broker
上,出于同样的减压策略,它也将同一个MessageQueue
均匀地分给消费者组的多台机器来消费。

正如上图显示的一样,理想情况下,应该是以这样理想
的方式来消费消息的:每个MessageQueue
都有各自的归属。
之前说过两种不同的消息消费模式:Push模式
和Pull模式
,其实它们本质上都是基于消费者主动发送拉取数据的请求来实现的,只不过在Push模式
下,Broker
发送消息的时效性更好。
在Push模式
下还有消费请求挂起
和长轮询
的机制。
消费请求挂起
是指当请求发送到Broker
后却没有新的消息可供处理时,RocketMQ就会将请求线程挂起(默认15秒)。当请求被挂起后,就会有一个后台
长轮询
线程定期去检查是否有新消息到来,如果有就唤醒挂起的线程,然后把消息返回回来。
消息读取
Broker
在收到拉取消息的请求之后,确切地说是某个具体的MessageQueue
(例如m0
)收到拉取消息的请求之后,它就会找到自己对应的ConsumeQueue
(例如c0
),然后Broker
将根据ConsumeQueue
中保存的元信息到CommitLog
中去寻找对应的消息数据,并返回给Consumer消费者
。当
Consumer消费者
消费完拉取到的数据后,会向Broker
提交一个消息消费进度
,其实就是一个称为ConsumeOffset
的元数据。这个提交消息消费进度
的过程就称之为ACK
。这样,当下次Consumer消费者
再执行拉取请求时,Broker
就知道从哪个MessageQueue
对应的ConsumeQueue
的哪个位置去找数据了。

当消费者
启动
、宕机
或者扩容
的时候,RocketMQ都会让各个消费者所在的机器执行一次称为Rebalance
的过程,它会为消费者重新分配可以处理的MessageQueue
。例如,如果上图中的机器4
宕机了,那么它之前负责处理的MessageQueue
就会被重新分配给机器3
;而如果机器4
恢复过来,那么之前的MessageQueue
可能又会被重新分配给它。虽然在不同的版本中这个Rebalance
的执行方式会稍有不同,但负载均衡的思想是一致的。消费者在拉取数据的时候,
Broker
会同时从磁盘
和Cache
中读取数据,具体过程是这样的。先从
Cache
中读取ConsumeQueue
的元信息。然后根据元信息再到
Cache
中读取CommitLog
。最后再从
磁盘
中读取完整的CommitLog
。
如果消费者能够紧跟生产者的步调,那么每次大概率都会从
Cache
中拿到的数据;反之,如果Broker
负载很高,或者消费者处理速率很低,那么拉取到的数据也大概率会是磁盘中的数据。