深入研究RocketMQ消费者是如何获取消息的
前言
小伙伴们,国庆都过的开心吗?国庆后的第一个工作日是不是很多小伙伴还沉浸在假期的心情中,没有工作状态呢?
那王子今天和大家聊一聊RocketMQ的消费者是如何获取消息的,通过学习知识来找回状态吧。
废话不多说,我们开始吧。
消费者组
首先我们了解一个概念,什么是消费者组。
消费者组你就可以把它理解为,给一组消费者起一个名字。
假设我们有一个订单Topic名字是OrderTopic,然后库存系统和积分系统都要消费这个Topic中的数据,我们分别给库存系统和积分系统起一个消费组名字:stock_consumer_group、integral_consumer_group。
设置消费者组名字是在代码中实现的,如下:
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("stock_consumer_group");
比如我们的库存系统提供了2台机器,每台机器上的消费者组名字都是stock_consumer_group,那么这2台机器就是一个消费者组。

大体结构如上图所示,那么当订单系统发送消息到OrderTopic中后,库存系统和积分系统是如何进行消费的呢?
默认情况下,这条消息发送到Broker后,库存系统和积分系统都会拉取这条消息,而且库存系统的两台机器中只有一台会消费到这条消息,积分系统也一样。
这就是消费组的概念,不同的系统设置不同的消费组,如果不同的消费组订阅了同一个Topic,那么对于Topic中的一条消息,每个消费组都会获取到这条消息。
集群模式和广播模式
接下来我们思考一个问题,对于消费者组而言,当它获取到一条消息后,假设消费者组内有多台机器,那么到底是只有一台机器获取到消息,还是所有机器都获取到消息呢?
这其实是消费的两种模式,集群模式和广播模式。
默认情况下我们都是使用的集群模式,也就是说消费者组收到消息后,只有其中的一台机器会接收到消息。
我们可以手动指定为广播模式。
consumer.setMessageModel(MessageModel.BROADCASTING)
指定为广播模式后,消费者组内的每台机器都会收到这条消息。
具体要根据业务场景选择消费模式。
MessageQueue与消费者的关系
接着我们想一下,对于一个Topic下的多个MessageQueue,消费者组中的多台机器是如何消费的呢?
这部分内容底层实现是很复杂的,我们可以简单的理解为它会均匀的将多个MessageQueue分配给消费者组中的多台机器消费。
举个例子,假如我们的OrderTopic有四个MessageQueue,这4个MessageQueue分布在两台MasterBroker上,每个MasterBroker上有两个MessageQueue。
然后库存系统作为一个消费者组有两台机器,那么最好的分配方式就是每台消费者机器负责两个MessageQueue,这样就实现了机器的负载消费,示意图如下:

所以我们可以大致的认为,一个Topic中的多个MessageQueue会被均匀的分布给一个消费者组中的多台机器进行消费,这里要注意一点,一个MessageQueue只能被一台消费者机器消费,但是一台消费者机器可以同时负责处理多个MessageQueue。
那么当消费者组中的机器数量发生变化时,是怎么处理的。
机器数量发生变化一般就两种情况,一种是有机器宕机了,另一种是增加机器进行集群扩容了。
其实这种情况下是会进行rebalance环节的,也就是会重新分配每个消费者机器要处理的MessageQueue。
Push模式和Pull模式
不知道小伙伴们还记不记得,在之前的文章RocketMQ的发送模式和消费模式中,我们已经用代码说明了消费者的两种消费模式:Push和Pull,当时只提供了Push消费的代码,而没有提供Pull消费的代码。
其实这两种模式本质上是一样的,都是消费者主动发出请求到Broker上拉取消息。
Push模式的底层也是通过消费者主动拉取的方式来实现的,只不过它的名字叫Push而已,意思是Broker尽可能实时的推送消息给消费者。
我们一般在使用RocketMQ的时候,消费模式基本都是使用的Push模式,因为Pull模式真的使用起来代码特别复杂,而且Push模式的底层还是Pull模式,只是对时效性有了更好的支持。
Push模式大体实现思路是这样的:当消费者发送请求到Broker拉取消息的时候,如果有新的消息可以消费,会立马返回消息到消费者进行消费,消费后会接着发送请求到Broker拉取消息。
也就说Push模式下,处理完一批消息后会理解再发送请求给Broker拉取下一批消息,所以时效性更好,看起来就像是Broker在实时推送消息。
当请求发送到Broker发现没有需要消费的消息时,就会让请求线程挂起,默认挂起15秒,然后会有另一个后台线程每隔一段时间判断一下是否有新消息需要消费,一旦发现了新的消息,就会去唤醒挂起的线程,将消息返回给消费者进行消费,然后消费完毕再次发送请求拉取消息。
这一部分的源码实现是很复杂的,我们只要了解它的核心思路就可以了。就算是Push模式,本质上也是对Pull模式的一种封装。
Broker如何读取消息返回给消费者
接下来我们来聊聊Broker是如何读取消息返回给消费者的。之前的文章深入研究Broker是如何持久化的中我们已经知道了Broker是如何持久化消息的,小伙伴们可以复习一下。
那么当消费者发送请求到Broker中拉取消息时,假设是第一次拉取,就会从MessageQueue中的第一条消息开始拉取。
如何定位到第一条消息的位置呢,首先Broker会找到MessageQueue对应的ConsumerQueue,从里面找到这条消息的offset,然后通过offset去CommitLog中读取消息数据,把消息返回给消费者。
当消费者消费完这条消息后,会提交一个消费的进度给Broker,Broker会记录下一个ConsumerOffset来标记我们的消费进度。
下次消费者再去这个MessageQueue中拉取消息时,就会从记录的消费位置继续拉取消息,而不用从头获取了。
总结
好了,到这里本篇文章就结束了。
今天主要和大家一起讨论了一下RocketMQ消费者的拉取和消费过程,也是国庆假期后的第一篇文章。
没有从国庆中收回心的小伙伴们(ps:王子也一样没有进入状态(`・ω・´))就与王子一起通过学习找回状态吧。
往期文章推荐:

深入研究RocketMQ消费者是如何获取消息的的更多相关文章
- JMS学习(八)-ActiveMQ Consumer 使用 push 还是 pull 获取消息
ActiveMQ是一个消息中间件,对于消费者而言有两种方式从消息中间件获取消息: ①Push方式:由消息中间件主动地将消息推送给消费者:②Pull方式:由消费者主动向消息中间件拉取消息.看一段官网对P ...
- rocketmq源码分析3-consumer消息获取
使用rocketmq的大体消息发送过程如下: 在前面已经分析过MQ的broker接收生产者客户端发过来的消息的过程,此文主要讲述订阅者获取消息的过程,或者说broker是怎样将消息传递给消费者客户端的 ...
- 深入研究RocketMQ生产者发送消息的底层原理
前言 hello,小伙伴们,王子又来和大家研究RocketMQ的原理了,之前的文章RocketMQ生产部署架构如何设计中,我们已经简单的聊过了生产者是如何发送消息给Broker的. 我们简单回顾一下这 ...
- RocketMQ消费者示例程序
转载请注明出处:http://www.cnblogs.com/xiaodf/ 本博客实现了一个简单的RocketMQ消费者的示例,MQ里存储的是经过Avro序列化的消息数据,程序读取数据并反序列化后, ...
- 转 Kafka、RabbitMQ、RocketMQ等消息中间件的对比 —— 消息发送性能和优势
Kafka.RabbitMQ.RocketMQ等消息中间件的对比 —— 消息发送性能和优势 引言 分布式系统中,我们广泛运用消息中间件进行系统间的数据交换,便于异步解耦.现在开源的消息中间件有很多,前 ...
- 基于Confluent.Kafka实现的KafkaConsumer消费者类和KafkaProducer消息生产者类型
一.引言 研究Kafka有一段时间了,略有心得,基于此自己就写了一个Kafka的消费者的类和Kafka消息生产者的类,进行了单元测试和生产环境的测试,还是挺可靠的. 二.源码 话不多说,直接上代码,代 ...
- RabbitMQ,RocketMQ,Kafka 几种消息队列的对比
常用的几款消息队列的对比 前言 RabbitMQ 优点 缺点 RocketMQ 优点 缺点 Kafka 优点 缺点 如何选择合适的消息队列 参考 常用的几款消息队列的对比 前言 消息队列的作用: 1. ...
- Rabbit mq订阅方式获取消息并可设置持久化
Rabbit 通过方式获取消息:订阅方式事实上是向queue注冊consumer,通过rpc向queue server发送注冊consumer的消息.rabbitMQ Server在收到消息后,依据消 ...
- RocketMQ 消费者
本文分析 DefaultMQPushConsumer,异步发送消息,多线程消费的情形. DefaultMQPushConsumerImpl MQClientInstance 一个客户端进程只有一个 M ...
随机推荐
- 【python】超有用的集合类collections,不来了解一下?
在使用Python的过程中,一定是离不开数据结构的, 也就是List-列表,Tuples-元组,Dictionaries-字典. 那实际应用中我们更多的还是要去操作这些结构里的数据.比如,在列表后面添 ...
- HDOJ 1003
动态规划一直AC不了竟然是因为一厢情愿的多加了一个#! printf("Case #%d:\n",count); --------------------------------- ...
- java-对象引用和对象本身
示意图 应用场景 1.赋值 只有用新的对象本身直接赋值给对应引用,该对象引用的值会改变,因为对象引用指向的对象本身改变了. 2.方法入参 调用方法和被调用方法. 代码 import com.aliba ...
- pyqt 设置QTabWidget标签页不可选
pyqt 设置QTabWidget标签页不可选 for i in range(1,7): self.tabWidget.setTabEnabled(i,False)i-对应标签页的位数
- 没事也来配一个logback
工程下载:https://files.cnblogs.com/files/xiandedanteng/logbackCfg20200115.zip 首先创建一个maven项目,pom.xml如下书写: ...
- linux:apache-配置基于域名的虚拟机主机
一个http服务要配置多个站点,就需要用到虚拟机主机. 虚拟机主机一般有三类:1.基于域名 2.基于端口 3.基于ip 举例操作基于域名配置三个站点: 域名 站点目录 www.eejind.com ...
- nodejs解压版安装和配置(带有搭建前端项目脚手架)
nodejs 安装 我先前用了nvm,觉得nvm挺厉害可以随时更换nodejs版本,但是研究了下,可能自己功力不够还是什么,并不好用,中间还出现了错误:所以最后还是卸载了: 本文图文并茂的一步一步的 ...
- Lua GC机制
说明 分析lua使用的gc算法,如何做到分步gc,以及测试结论 gc算法分析 lua gc采用的是标记-清除算法,即一次gc分两步: 从根节点开始遍历gc对象,如果可达,则标记 遍历所有的gc对象,清 ...
- vsCode 设置vue文件标签内的style智能提示
VS Code 文件->首选项->设置 搜索:files.associations 点击在setting.json中编辑 最后一行添加配置: "files.association ...
- Mqtt协议 服务器交互
1.服务器发送消息 package demo1; import org.eclipse.paho.client.mqttv3.MqttClient; import org.eclipse.paho.c ...