RocketMQ的消费流程和最佳实践-学习笔记

消费者是相对第2章的生产者而言的,逻辑更加复杂。

本章主要讲解的核心内容有:
● 消费者默认的两个实现类。
● 消费者的启动过程。
● 消息的消费过程。
● 可靠消费。

● 消费进度保存过程。
● 消息过滤。
RocketMQ的消费流程和最佳实践-学习笔记_第1张图片
RocketMQ的消费流程和最佳实践-学习笔记_第2张图片

RocketMQ的消费流程和最佳实践-学习笔记_第3张图片

                                        3.1.3 可靠消费
RocketMQ的消费流程和最佳实践-学习笔记_第4张图片

RocketMQ的消费流程和最佳实践-学习笔记_第5张图片

2.Rebalance机制
Rebalance(重平衡)机制,用于在发生Broker掉线、Topic扩容和缩容、消费者扩容和缩容等变化时,自动感知并调整自身消费,以尽量 减少甚至避免消息没有被消费。后面会详细讲述Rebalance的过程。
 
3.2 消费者启动机制
RocketMQ 客 户 端 中 有 两 个 独 立 的 消 费 者 实 现 类 :
org.apache.rocketmq.client.consumer.DefaultMQPullConsumer 和
org.apache.rocketmq.client.consumer.DefaultMQPushConsumer 。 下
面将分别进行介绍。
1.DefaultMQPullConsumer
该消费者使用时需要用户主动从 Broker 中 Pull 消息和消费消 息,提交消费位点。DefaultMQPullConsumer的类图继承关系如图3-6所示。
RocketMQ的消费流程和最佳实践-学习笔记_第6张图片

 
可以看到,DefaultMQPullConsumer 实现时包含消费者的操作和属 性配置,这是一个典型的类对象设计。下面我们介绍一些核心属性和方 法。
以下是一些核心属性:
namesrvAddr: 继承自 ClientConfig,表示 RocketMQ 集群的 Namesrv 地址,如果是多个,则用分号分开。比如:127.0.0.1:9876;127.0.0.2:9876。
clientIP: 使用客户端的程序所在机器的IP地址,目前支持IPV4 和IPV6,同时排除了本地环回地址(127.0.xxx.xxx)和私有内网地址 192.168.xxx.xxx)。如果在 Docker 中运行,获取的IP地址是容器 所在的IP地址,而非宿主主机的IP地址。
instanceName: 实例名,顾名思义每个实例都需要取不一样的名 字。假如要在同一个机器上部署多个程序进程,那么每个进程的实例名都必须不相同,否则程序会启动失败。
vipChannelEnabled: 这是一个 boolean 值,表示是否开启 VIP 通道。VIP 通道和非VIP通道的区别是使用不同的端口号进行通信。
clientCallbackExecutorThreads: 客户端回调线程数。该线程数等于Netty 通 信 层 回 调 线 程 的个数 ,默 认 值 为 Runtime.getRuntime().availableProcessors(),表示当前有效的 CPU个数。
pollNameServerInterval :获取Topic路由信息间隔,单位为ms, 默认为30 000ms。
heartbeatBrokerInterval: 客户端和Broker心跳间隔,单位为 ms,默认为30 000ms。
persistConsumerOffsetInterval :持久化消费位点时间间隔,单位为 ms,默认为5000ms。
defaultMQPullConsumerImpl: 默认Pull消费者的具体实现。 consumerGroup: 消费者组名字。
brokerSuspendMaxTimeMillis 在长轮询模式下,Broker的最大挂起请求时间,建议不要修改此值。
consumerTimeoutMillisWhenSuspend: 在长轮询模式下,消费者的最大请求超时时间,必须比brokerSuspendMaxTimeMillis大,不建议 修改。
consumerPullTimeoutMillis: 消费者Pull消息时Socket的超时时 间。
messageModel: 消费模式,现在支持集群模式消费和广播模式消 费。
messageQueueListener :消息路由信息变化时回调处理监听器,一般在重新平衡时会被调用。
offsetStore: 位点存储模块。集群模式位点会持久化到Broker中,广播模式持久化到本地文件中,位点存储模块有两个实现类:
RemoteBrokerOffsetStore 和LocalFileOffsetStore。
allocateMessageQueueStrategy: 消费Queue分配策略管理器。
maxReconsumeTimes: 最大重试次数,可以配置。
下面介绍一些核心方法。由于生产者和消费者都继承了MQAdmin接 口,所以管理相关的接口都是一样的,不再赘述。registerMessageQueueListener(): 注册队列变化监听器,当 队列发生变化时会被监听到。
pull(): 从Broker中Pull消息,如果有PullCallback参数,则表示异步拉取。
pullBlockIfNotFound(): 长轮询方式拉取。如果没有拉取到消息,那么Broker会将请求Hold住一段时间。
updateConsumeOffset ( final MessageQueue mq , final long offset):更新某一个Queue的消费位点。
fetchConsumeOffset(final MessageQueue mq,final boolean fromStore):查找某个Queue的消费位点。
sendMessageBack(MessageExt msg,int delayLevel,String
brokerName,String consumerGroup): 如果消费发送失败,则可以将消息重新发回给 Broker,这个消费者组延迟一段时间后可以再消费 也就是重试)。
fetchSubscribeMessageQueues(final String topic): 获取 一个Topic的全部Queue信息。
2.DefaultMQPushConsumer
DefaultMQPushConsumer 的 大 部 分 属 性 、 方 法 和
DefaultMQPullConsumer 是 一 样 的 。 下 面 介 绍 一 下
DefaultMQPushConsumer的核心属性和方法。
defaultMQPushConsumerImpl: 默认的Push消费者具体实现类。 consumeFromWhere :一个枚举,表示从什么位点开始消费。
(1)CONSUME_FROM_LAST_OFFSET:从上次消费的位点开始消费, 相当于断点继续。
( 2 ) CONSUME_FROM_LAST_OFFSET_AND_FROM_MIN_WHEN_BOOT_FIRSTRoketMQ
4.2.0不支持,处理同CONSUME_FROM_LAST_OFFSET。
(3) CONSUME_FROM_MIN_OFFSET:RoketMQ 4.2.0 不支持,处理 同CONSUME_FROM_LAST_OFFSET。
(4) CONSUME_FROM_MAX_OFFSET:RoketMQ 4.2.0 不支持,处理
同CONSUME_FROM_LAST_OFFSET。
(5)CONSUME_FROM_FIRST_OFFSET:从ConsumeQueue的最小位点开 始消费。
(6)CONSUME_FROM_TIMESTAMP:从指定时间开始消费。
consumeTimestamp : 表 示 从 哪 一 时 刻 开 始 消 费 , 格 式 为
yyyyMMDDHHmmss , 默 认 为 半 小 时 前 。 当
consumeFromWhere=CONSUME_FROM_TIMESTAMP 时,consumeTimestamp设 置的值才生效。
allocateMessageQueueStrategy: 消费者订阅topic-queue策略。
subscription: 订阅关系,表示当前消费者订阅了哪些Topic的哪 些Tag。messageListener: 消息Push回调监听器。
consumeThreadMin : 最 小 消 费 线 程 数 , 必 须 小 于
consumeThreadMax。
consumeThreadMax :最大线程数,必须大于consumeThreadMin。
adjustThreadPoolNumsThreshold: 动态调整消费线程池的线程数大小,开源版本不支持该功能。
consumeConcurrentlyMaxSpan :并发消息的最大位点差。如果 Pull消息的位点差超过该值,拉取变慢。
pullThresholdForQueue :一个 Queue 能缓存的最大消息数。超 过该值则采取拉取流控措施。
pullThresholdSizeForQueue :一个Queue最大能缓存的消息字节数,单位是MB。
pullThresholdForTopic :一个Topic最大能缓存的消息数。超过 该 值 则 采 取 拉 取 流 控 措 施 。 该 字 段 默 认 值 是 -1 , 该 值 根 据
pullThresholdForQueue 的 配 置 决 定 是 否 生 效 ,
pullThresholdForTopic的优先级低于pullThresholdForQueue。
pullThresholdSizeForTopic :一个Topic最大能缓存的消息字节数,单位是MB。默认为-1,结合 pullThresholdSizeForQueue 配置项 生效,该配置项的优先级低于pullThresholdSizeForQueue。
pullInterval :拉取间隔,单位为ms。 consumeMessageBatchMaxSize: 消费者每次批量消费时,最多消 费多少条消息。
pullBatchSize :一次最多拉取多少条消息。
postSubscriptionWhenPull :每次拉取消息时是否更新订阅关系,该方法的返回值默认为False。
maxReconsumeTimes :最大重试次数,该函数返回值默认为-1,表示默认最大重试次数为16。
suspendCurrentQueueTimeMillis: 为短轮询场景设置的挂起时间,比如顺序消息场景。
consumeTimeout :消费超时时间,单位为min,默认值为15min。
上面主要讲了RocketMQ默认的两种消费者的核心属性和方法,下面来看一下它们是如何启动的。
DefaultMQPullConsumer的启动流程如图3-7所示。 业务代码通常使用构造函数初始化一个DefaultMQPullConsumer实 例,设置各种参数,比如Namesrv地址、消费者组名等。然后调用
start()方法启动defaultMQPullConsumerImpl实例。我们这里主要讲
defaultMQPullConsumerImpl.start()方法中的启动过程,具体步骤如下:
第 一 步 : 最 初 创 建 defaultMQPullConsumerImpl 时 的 状 态 为
ServiceState.CREATE_JUST,然后设置消费者的默认启动状态为失败。
RocketMQ的消费流程和最佳实践-学习笔记_第7张图片

 
第二步:检查消费者的配置比,如消费者组名、消费类型、Queue 分配策略等参数是否符合规范;将订阅关系数据发给Rebalance服务对 象。
第三步:校验消费者实例名,如果是默认的名字,则更改为当前的 程序进程id。
第四步:获取一个 MQClientInstance,如果 MQClientInstance 已经初始化,则直接返回已初始化的实例。这是核心对象,每个 clientId缓存一个实例。
第五步:设置Rebalance对象消费者组、消费类型、Queue分配策略、MQClientInstance等参数。
第六步:对 Broker API 的封装类 pullAPIWrapper进行初始化, 同时注册消息,过滤filter。
第七步:初始化位点管理器,并加载位点信息。位点管理器分为本地管理和远程管理两种,集群消费时消费位点保存在 Broker 中,由远程管理器管理;广播消费时位点存储在本地,由本地管理器管理。
第八步:本地注册消费者实例,如果注册成功,则表示消费者启动成功。
第九步:启动MQClientInstance实例。具体启动过程见2.2节。
DefaultMQPushConsumer的启动过程如图3-8所示。 DefaultMQPushConsumer的启动过程与DefaultMQPullConsumer的启 动 过 程 类 似 , 用 户 也 是 通 过 构 造 函 数 初 始 化 , 依 次 调 用
DefaultMQPushConsumer 的 start 方 法 和 其 内 部 实 现 类
DefaultMQPushConsumerImpl的start()方法,开启整个启动过程的。
DefaultMQPushConsumer 的启动过程分为11个步骤,前7个步骤与
DefaultMQPullConsumer的步骤类似,不再赘述。
第八步:初始化消费服务并启动。之所以用户“感觉”消息是 Broker 主动推送给自己的,是因为DefaultMQPushConsumer通过Pull服务将消息拉取到本地,再通过Callback的 形 式,将本地消息Push给用 户的消费代码。DefaultMQPushConsumer 与DefaultMQPullConsumer获 取消息的方式一样,本质上都是拉取
RocketMQ的消费流程和最佳实践-学习笔记_第8张图片

消费服务分为两种,即并行消费服务和顺序消费服务,对应的实现
类 分 别 是 org.apache.rocketmq.client.impl.consumer.ConsumeMessageConcurr
entlyService 和 org.apache.rocketmq.client.impl.consumer.ConsumeMessageOrderly
Service。DefaultMQPushConsumer根据用户监听器继承的不同接口初始 化不同的消费服务程序,具体的实现代码如下:

RocketMQ的消费流程和最佳实践-学习笔记_第9张图片

第九步:启动MQClientInstance实例。具体启动过程见2.1.3节。
第十步:更新本地订阅关系和路由信息;通过 Broker 检查是否支 持消费者的过滤类型;向集群中的所有Broker发送消费者组的心跳信 息。
第十一步:立即执行一次Rebalance,Rebalance过程我们在下一节中详细讲解。
3.3 消费者的Rebalance机制
客户端是通过Rebalance服务做到高可靠的。当发生Broker掉线、 消费者实例掉线、Topic 扩容等各种突发情况时,消费者组中的消费者 实例是怎么重平衡,以支持全部队列的正常消费的呢?
我们先看看Rebalance服务的类图,如图3-9所示。
如图 3-9 所示,RebalancePullImpl 和 RebalancePushImpl 两个重 平 衡 实 现 类 , 分 别 被 DefaultMQPullConsumer 和 DefaultMQPushConsumer 使用。下面讲一下 Rebalancelmpl 的核心属性和方法。

RocketMQ的消费流程和最佳实践-学习笔记_第10张图片

1.Rebalancelmpl的核心属性 ConcurrentMap<MessageQueue , ProcessQueue>
processQueueTable :记 录MessageQueue和 ProcessQueue的关系。
MessageQueue 可 以 简 单 地 理 解 为 ConsumeQueue 的 客 户 端 实 现 ;
ProcessQueue是保存Pull消息的本地容器。
ConcurrentMap<String , Set<MessageQueue>> topicSubscribeInfoTable : Topic 路 由 信 息 。 保 存 Topic 和 MessageQueue的关系。
ConcurrentMap<String/*topic*/ , SubscriptionData> subscriptionInner:真正的订阅关系,保存当前消费者组订阅了哪些 Topic的哪些Tag。
AllocateMessageQueueStrategy allocateMessageQueueStrategy:MessageQueue 消费分配略的实 现。
MQClientInstance mQClientFactory: client实例对象,具体讲 解见4.1.3节。
2.Rebalancelmpl核心方法 boolean lock(final MessageQueue mq):为MessageQueue加
锁。
void doRebalance(final boolean isOrder):执行Rebalance 操作。
void messageQueueChanged(final String topic,final Set <MessageQueue>mqAll , final Set<MessageQueue>mqDivided): 通知Message发生变化,这个方法在Push和Pull两个类 中被重写。
boolean removeUnnecessaryMessageQueue ( final MessageQueue mq , final ProcessQueuepq ) : 去 掉 不 再 需 要 的 MessageQueue。
void dispatchPullRequest ( final List<PullRequest> pullRequestList):执行消息拉取请求。
boolean updateProcessQueueTableInRebalance(final String topic , final Set<MessageQueue>mqSet , final boolean isOrder):在Rebalance中更新processQueue。
Rebalancelmpl 、 RebalancePushImpl 、 RebalancePullImpl 是 Rebalance的核心实现,主要逻辑都在Rebalancelmpl中,因为Pull消费 者和Push消费者对Rebalance的需求不同,在各自的实现中重写了部分 方法,以满足自身需求。
如果有一个消费者实例下线了,Broker和其他消费者是怎么做 Rebalance的呢?图3-10展示了整个Rebalance的过程。

RocketMQ的消费流程和最佳实践-学习笔记_第11张图片

RocketMQ的消费流程和最佳实践-学习笔记_第12张图片
下面介绍一下doRebalance()方法的实现逻辑,主要有以下几个 步骤。第一步:查找当前 clientId 对应的全部的消费者组,全部执行 一次 Rebalance。虽然消费者实现分为 Pull消费和Push消费两种默认实现,调用的是不同实现类中的Rebalance方法,但是实现逻辑都差不多,下面笔者将以Push消费者为例继续讲解。
第 二 步 : 判 断 Rebalance 开 关 , 如 果 没 有 被 暂 停 , 则 调 用 RebalancePushImpl.rebalance()方法。 第三步:在RebalancePushImpl.rebalance()方法中,获取当前消费者全部订阅关系中的Topic,循环对每个Topic进行Rebalance。待 全部的Rebalance都执行完后,将不属于当前消费者的队列删除。
第四步:Topic队列重新分配。这里也就是客户端Rebalance的核心逻辑之处。根据是集群消费还是广播消费分别执行 MessageQueue 重新分配的逻辑。下面以集群消费为例进行讲解。
(1)获取当前Topic的全部MessageQueue(代码中是mqSet)和该Topic的所有消费者的clientId(代码中是cidAll)。只有当两者都不 为空时,才执行Rebalance。具体实现代码如下:
RocketMQ的消费流程和最佳实践-学习笔记_第13张图片

RocketMQ的消费流程和最佳实践-学习笔记_第14张图片

(4)动态更新ProcessQueue。在队列重新分配后,当前消费者消 费的队列可能不会发生变化,也可能发生变化,不管是增加了新的队列 需 要 消 费 , 还 是 减 少 了 队 列 , 都 需 要 执 行
updateProcessQueueTableInRebalance()方法来更新ProcessQueue。
如 果 有 MessageQueue 不 再 分 配 给 当 前 的 消 费 者 消 费 , 则 设 置 ProcessQueue.setDropped(true),表示放弃当前MessageQueue 的 Pull 消息。updateProcessQueueTableInRebalance()方法的具体实现代码如下:
RocketMQ的消费流程和最佳实践-学习笔记_第15张图片

RocketMQ的消费流程和最佳实践-学习笔记_第16张图片

RocketMQ的消费流程和最佳实践-学习笔记_第17张图片

RocketMQ的消费流程和最佳实践-学习笔记_第18张图片

 RocketMQ的消费流程和最佳实践-学习笔记_第19张图片

RocketMQ的消费流程和最佳实践-学习笔记_第20张图片

RocketMQ的消费流程和最佳实践-学习笔记_第21张图片 RocketMQ的消费流程和最佳实践-学习笔记_第22张图片

RocketMQ的消费流程和最佳实践-学习笔记_第23张图片

理论上位点信息越是及时上报 Broker,越能减少消息重复的可能性。RocketMQ在设计时并不完全支持Exactly-Once的语义,因为实现该语义的代价颇大,并且使用场景极少,再加上用户侧实现幂等的代价更 小,故而RocketMQ在设计时将幂等操作交与用户处理。

RocketMQ的消费流程和最佳实践-学习笔记_第24张图片

 RocketMQ的消费流程和最佳实践-学习笔记_第25张图片

RocketMQ的消费流程和最佳实践-学习笔记_第26张图片

 RocketMQ的消费流程和最佳实践-学习笔记_第27张图片

RocketMQ的消费流程和最佳实践-学习笔记_第28张图片

 RocketMQ的消费流程和最佳实践-学习笔记_第29张图片

RocketMQ的消费流程和最佳实践-学习笔记_第30张图片

RocketMQ的消费流程和最佳实践-学习笔记_第31张图片

RocketMQ的消费流程和最佳实践-学习笔记_第32张图片

 RocketMQ的消费流程和最佳实践-学习笔记_第33张图片

 RocketMQ的消费流程和最佳实践-学习笔记_第34张图片

 RocketMQ的消费流程和最佳实践-学习笔记_第35张图片

 RocketMQ的消费流程和最佳实践-学习笔记_第36张图片

 RocketMQ的消费流程和最佳实践-学习笔记_第37张图片

RocketMQ的消费流程和最佳实践-学习笔记_第38张图片

RocketMQ的消费流程和最佳实践-学习笔记_第39张图片

RocketMQ的消费流程和最佳实践-学习笔记_第40张图片

RocketMQ的消费流程和最佳实践-学习笔记_第41张图片

RocketMQ的消费流程和最佳实践-学习笔记_第42张图片

做完这两个操作后,我们试想一下,消费线程在下一次消费时会发 生什么事情?如果是从msgTreeMap中获取一批消息,那么返回的消息又是哪些呢?消息物理位点最小的,也就是之前未成功消费的消息。如果 顺序消息消费失败,会再次投递给消费者消费,直到消费成功,以此来
保证顺序性。

RocketMQ的消费流程和最佳实践-学习笔记_第43张图片

 RocketMQ的消费流程和最佳实践-学习笔记_第44张图片

RocketMQ的消费流程和最佳实践-学习笔记_第45张图片

RocketMQ的消费流程和最佳实践-学习笔记_第46张图片

RocketMQ的消费流程和最佳实践-学习笔记_第47张图片

 RocketMQ的消费流程和最佳实践-学习笔记_第48张图片

RocketMQ的消费流程和最佳实践-学习笔记_第49张图片

RocketMQ的消费流程和最佳实践-学习笔记_第50张图片

RocketMQ的消费流程和最佳实践-学习笔记_第51张图片

RocketMQ的消费流程和最佳实践-学习笔记_第52张图片

 RocketMQ的消费流程和最佳实践-学习笔记_第53张图片

你可能感兴趣的:(rocketmq,java,服务器,系统架构,分布式)