面向面试编程:RocketMQ——消费者处理消息

280 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第24天,点击查看活动详情

面试官:RocketMQ消费者是如何获取消息处理以及进行ACK的?

消费组

消费者组就是给一组消费者起一个名字。

不同的系统应该设置不同的消费组,如果不同的消费组订阅了同一个Topic,对Topic里的一条消息,每个消费组都会获取到这条消息。

对于一个消费组而言,他获取到一条消息之后,如果消费组内部有多台机器,到底是只有一台机器可以获取到这个消息,还是每台机器都可以获取到这个消息?这个就是集群模式和广播模式的区别。

默认情况下我们都是集群模式,也就是说,一个消费组获取到一条消息,只会交给组内的一台机器去处理,不是每台机器都可以获取到这条消息的。

如果修改为广播模式,那么对于消费组获取到的一条消息,组内每台机器都可以获取到这条消息。但是相对而言广播模式其实用的很少,常见基本上都是使用集群模式来进行消费的。

MessageQueue与消费者的关系

面试官:对于一个Topic上的多个MessageQueue,是如何由一个消费组中的多台机器来进行消费的呢?

我们可以简单的理解为,他会均匀的将MessageQueue分配给消费组的多台机器来消费。大致可以认为一个Topic的多个MessageQueue会均匀分摊给消费组内的多个机器去消费,这里的一个原则就是,一个MessageQueue只能被一个消费机器去处理,但是一台消费者机器可以负责多个MessageQueue的消息处理。

此时就要涉及到两种消费模式了,一个是Push,一个是Pull。

实际上,这两个消费模式本质是一样的,都是消费者机器主动发送请求到Broker机器去拉取一批消息下来。Push消费模式本质底层也是基于这种消费者主动拉取的模式来实现的,只不过他的名字叫做Push而已,意思是Broker会尽可能实时的把新消息交给消费者机器来进行处理,他的消息时效性会更好。

一般我们使用RocketMQ的时候,消费模式通常都是基于他的Push模式来做的,因为Pull模式的代码写起来更加的复杂和繁琐,而且Push模式底层本身就是基于消息拉取的方式来做的,只不过时效性更好而已。

Push模式的实现思路:当消费者发送请求到Broker去拉取消息的时候,如果有新的消息可以消费那么就会立马返回一批消息到消费机器去处理,处理完之后会接着立刻发送请求到Broker机器去拉取下一批消息。所以消费机器在Push模式下会处理完一批消息,立马发起请求拉取下一批消息,消息处理的时效性非常好,看起来就跟Broker一直不停的推送消息到消费机器一样。

当你的请求发送到Broker,结果他发现没有新的消息给你处理的时候,就会让请求线程挂起,默认是挂起15秒,然后这个期间他会有后台线程每隔一会儿就去检查一下是否有的新的消息给你,另外如果在这个挂起过程中,如果有新的消息到达了会主动唤醒挂起的线程,然后把消息返回给你。

Broker将消息读取出来返回给消费机器

面试官:Broker在收到消费机器的拉取请求之后,是如何将消息读取出来返回给消费机器的?

其实这里要涉及到两个概念,分别是ConsumeQueue和CommitLog。

假设一个消费者机器发送了拉取请求到Broker了,他说我这次要拉取MessageQueue中的消息,然后我之前都没拉取过消息,所以就从这个MessageQueue中的第一条消息开始拉取好了。于是,Broker就会找到MessageQueue对应的ConsumeQueue,从里面找到第一条消息的offset。接着Broker就需要根据ConsumeQueue中找到的第一条消息的地址,去CommitLog中根据这个offset地址去读取出来这条消息的数据,然后把这条消息的数据返回给消费者机器。所以其实消费消息的时候,本质就是根据你要消费的MessageQueue以及开始消费的位置,去找到对应的ConsumeQueue读取里面对应位置的消息在CommitLog中的物理offset偏移量,然后到CommitLog中根据offset读取消息数据,返回给消费者机器。

消费者消费消息

面试官:消费者机器如何处理消息、进行ACK以及提交消费进度?

当我们处理完这批消息之后,消费者机器就会提交我们目前的一个消费进度到Broker上去,然后Broker就会存储我们的消费进度。那么下次这个消费组只要再次拉取这个ConsumeQueue的消息,就可以从Broker记录的消费位置开始继续拉取,不用重头开始拉取了。

面试官:如果消费组中出现机器宕机或者扩容加机器的情况,他会怎么处理?

这个时候其实会进入一个rabalance的环节,也就是说重新给各个消费机器分配他们要处理的MessageQueue。