- 消息的消费者,从消息获取方式分为:
- 推
- 拉 - 推包含了拉,所以拉不再单独说明
- 推 com.alibaba.rocketmq.client.consumer.DefaultMQPushConsumer
- 从使用上来看,数据是broker端源源不断的将消息推到消费者
- 从消息的消费方式上,分为:
- 集群消费:所有的消费者平均消费一份消息
- 广播消费:每个消费者各自消费同一份消息
- 从消费的顺序上,又可分为:
- 并发消费
- 顺序消费
- 集群并发推方式的消费实现:
- 首先需要介绍一下rebalance,何谓rebalance?
rebalance即重新平衡的意思,它是实现集群消费的关键
如何进行rebalance呢?参见下图:
前提1: consumer会每30s从nameserver上更新所订阅的topic路由列表,同时会更新broker路由->brokerName与角色和地址关系
前提2: consumer会每30s与所有broker进行一次心跳,将clientid,topic,group发送至broker
rebalance开始: consumer会每10s进行一次rebalance
1 首先选择一个broker,根据topic和group查询到订阅此topic的属于同一group的所有的consumer clientid
2 根据rebalance策略,进行分配.rebalance完毕之后,就可以知道当前的consumer消费哪些broker的哪些queue
3 这个对应关系呢,对应着一个重要的对象,如下图,对于上面rebalance之后的结果会产生8个PullRequest对象,分别对应8个队列:
- 接着来看消息拉取的流程:
- PullMessageService为一个阻塞队列服务,其内有一个线程不断轮询队列,获取PullRequest对象,调用DefaultMQPushConsumerImpl进行消息拉取
- DefaultMQPushConsumerImpl消息拉取前进行状态,参数校验和准备,并负责构造回调实例PullCallback进行消息的消费. 调用PullAPIWrapper进行消息拉取
- PullAPIWrapper主要负责broker路由的解析,压力均衡
- NettyRemotingClient为netty封装,负责与broker通信,并在结果返回后回调PullCallback
- PullCallback对返回的结果进行解码,更新下次拉取的偏移量,并调用ConsumeMessageService触发客户端消费,它还会将PullRequest放回PullMessageService,从而形成一个循环.
- ConsumeMessageService负责调用客户端的并发消费或顺序消费,并处理消费的结果.
- 对于广播模式,消费失败的消息不作处理
- 对于集群模式,消费失败的发到重试队列,发送失败的尝试让客户端继续消费
- 对于集群模式,消费失败的消息且重试次数>16,则发往DLQ,表示该消息不再重试,为死消息.
-
3.a rebalance流程中,产生的PullRequest对象,其中的属性为netxOffset,之后拉取消息都会以此偏移量为标准拉取,那么它从那里来的?
/** * Consumer从哪里开始消费<br> * * @author shijia.wxr<vintage.wang@gmail.com> */ public enum ConsumeFromWhere { /** * 一个新的订阅组第一次启动从队列的最后位置开始消费<br> * 后续再启动接着上次消费的进度开始消费 */ CONSUME_FROM_LAST_OFFSET, /** * 一个新的订阅组第一次启动从队列的最前位置开始消费<br> * 后续再启动接着上次消费的进度开始消费 */ CONSUME_FROM_FIRST_OFFSET, /** * 一个新的订阅组第一次启动从指定时间点开始消费<br> * 后续再启动接着上次消费的进度开始消费<br> * 时间点设置参见DefaultMQPushConsumer.consumeTimestamp参数 */ CONSUME_FROM_TIMESTAMP, }
rebalance中,如果topic路由发生变化,会新生成PullRequest,其中的属性netxOffset采用用户设置的ConsumeFromWhere,计算响应的偏移量.
-
首先说下集群消费模式下的offset请求:
-
针对返回的offset>=0的情况,三种消费位置都直接接受,即从offset处消费
- 只有对于offset<0的情况(即这个topic的queue没有被此group消费过,或者说这个group第一次启动)不一样:
-
CONSUME_FROM_LAST_OFFSET: 会继续拉取队列的最大偏移量作为消费的起始位置
-
CONSUME_FROM_FIRST_OFFSET: 消费的起始位置为0
-
CONSUME_FROM_TIMESTAMP: 首先根据时间戳定位到consumerqueue的位置文件上,通过二分查找法遍历commitlog中的消息的时间戳,查找起始位置
-
-
广播模式与集群模式类似,唯一不同的是不会从broker查询,而是从本地文件查询offset
-
- 消息拉取流程中的偏移量:
- 由上面的消息拉取流程(DefaultMQPushConsumerImpl)可知,拉取前有两个偏移量:
- PullRequest.nextOffset 表示下次拉取的偏移量
- commitOffset (OffsetStore.offset) 表示当前已经消费的偏移量
- 通过一个具体的流程来看下这两个偏移量的具体含义,第一次消息拉取后,netxOffset变成了2:
- 第二次消息拉取后,netxOffset变成了4,假设消费者消费的比较慢,所以commitOffset在第二次中仍是0:
- 假设第三次拉取时,客户端消费了第一次拉取的消息那么commitOffset=2,故broker会存储客户端的消费偏移量:
- 此时可以知道,rebalance时,从broker拉取的offset就是commitOffset.
- 另外,客户端还会每隔5秒,将offset全量同步至broker一次.
- 如果broker为master,默认每次拉取消息时发送存储commitOffset的标志,如果为slave,则不发送该标志.
- 由上面的消息拉取流程(DefaultMQPushConsumerImpl)可知,拉取前有两个偏移量:
- 高可用
- 自身高可用
- 假如只有一个consumer,挂了,那么再起来后会从broker上拉取最后一次同步的offset继续消费
- 假如两个consumer,那么两个consumer会均分queue来消费,如果其中一个挂了,另外一个会消费挂了的consumer对应的queue,offset同样会接着挂了conumser的最后一次同步的offset继续消费. 假如挂了queue再起来,同样会均分queue, offset会取最后一次同步的offset来消费.
- 可能存在重复消费,需要消费端自己做幂等处理.
- nameserver挂了的影响
- 由于consumer会从nameserver列表中顺序选一个进行连接,并定时查询topic的路由信息,如果正在连接的nameserver挂了,会选择下一个nameserver进行连接
所以nameserver挂了并不会影响consumer
- 由于consumer会从nameserver列表中顺序选一个进行连接,并定时查询topic的路由信息,如果正在连接的nameserver挂了,会选择下一个nameserver进行连接
- master挂了的影响
- consumer拉取消息异常
- 心跳异常
- rebalance异常(根据topic和group从某一个broker查询所有的consumer id)
- offset更新任务异常
- 但是topic路由的定时任务(从nameserver更新)最终将master地址移除:
- consumer解析新的关系,从slave拉数据,offset为客户端异常时的offset
- 以上异常消失
- 此时如果master起来
- topic路由的定时任务(从nameserver更新)最终将master地址获取
- consumer解析新的关系,从master拉数据,,offset为客户端最后消费的offset
- slave挂了的影响
- 只要master不挂,基本没有影响
- 自身高可用
- 首先需要介绍一下rebalance,何谓rebalance?
- 集群顺序推方式的消费实现:
- 此种方式跟 集群并发推方式 区别在于一种是无序消息,一种是顺序消息.
- 顺序消息的消费:
- com.alibaba.rocketmq.client.impl.consumer.ConsumeMessageOrderlyService
- 对于顺序消费,ConsumeMessageOrderlyService会定时远程锁定它所拥有的MessgeQueue,即 保证同一时刻, group<->topic<->queueid<->client是唯一的
- 在消息被拉取到本地后(参见上面的 消息拉取的流程的ConsumerMessgeService部分), 锁定本地的ProcessQueue, 由于ProcessQueue内部为TreeMap,故可以保证消息的顺序.
- 锁定后其余消费及offset更新等与并发消费一致
- com.alibaba.rocketmq.client.impl.consumer.ConsumeMessageOrderlyService
8.consumer
最新推荐文章于 2023-06-28 23:58:35 发布