rocketmq的消费者源码解读一(start方法)

以下仅分析消费者部分,公共代码在生产者和namesrv中已分析;

1)创建DefaultMQPushConsumer实例

入参为消费者组名,其中会创建DefaultMQPushConsumerImpl实例,其构造方法没做啥。有一个实例变量进行了自定义初始化,即new RebalancePushImpl,其中又会调用RebalanceImpl的构造方法,此时会初始化三个重要的实例变量;

1.1)processQueueTable

chm类型的,键值对为<MessageQueue,ProcessQueue>;

1.2)topicSubscribeInfoTable

chm类型的,键值对为<String,Set>;

1.3)subscriptionInner;

chm类型的,键值对为<String,SubscriptionData>;
另外在DefaultMQPushConsumer的构造方法中也会实例化AllocateMessageQueueAveragely作为默认负载策略;DefaultMQPushConsumer类继承了ClientConfig类,实际上与配置有关;DefaultMQPushConsumer类充当了门面角色,采用了门面设计模式;

2)设置默认消费起点CONSUME_FROM_FIRST_OFFSET

暂无补充;

3)订阅topic和设置过滤

暂无补充;

4)注册监听处理拉取到的消息

调用了DefaultMQPushConsumer#registerMessageListener方法,入参为MessageListenerConcurrently实例或MessageListenerOrderly实例;接着会重写consumeMessage方法,拿到消息进行业务处理;

5)consume.start

启动哪些:客户端netty,定时任务,一些线程等;这里很多与生产者逻辑一致,不赘述,重点分析消费者特有部分,和生产者相同的共有的逻辑省略;

5.1)checkConfig和判断serviceState状态

checkConfig方法中消费者组不能为null;组名不能为默认值;消费模式不能为null;consumeFromWhere不能为null;启动时间不能为null;分配策略不能为null;订阅信息不能为null;messageListenner不能为null,要么是顺序消费,要么是并发消费,否则抛异常;消费服务的线程数介于1到1000之间,否则抛异常等等;以上为null的均抛相应异常;
状态判断和生产者的逻辑一致;

5.2)copySubscription()

根据topic和tag构建订阅信息实例SubscriptionData实例,其中含有分别存储tag和tag.hashCode的set集合;最后将<topic,subscriptionData>键值对放入subscriptionInner集合中;

5.3)创建MQClientInstance实例

若消费者和生产者所处机器ip的不同,则此时创建实例和生产者是不同实例;逻辑一样;这是个公共类;在创建客户端实例时,会执行以下三个与消费者相关的实例化操作:

5.3.1)实例化PullMessageService

会初始化pullRequestQueue队列,是LinkedBlockingQueue类型;初始化调度任务线程;

5.3.2)实例化RebalanceService

并未实例化什么重要的;

5.3.3)实例化负责消息回退的内部发送者DefaultMQProducer

构造方法入参是CLIENT_INNER_PRODUCER_GROUP,表示生产者组名;当执行该内部生产者实例的start方法时,入参传入的是false,当检测到是生产者组名是CLIENT_INNER_PRODUCER_GROUP时,就会不新建新的客户端实例;另外检测到入参为false,则不会执行mQClientInstance的start方法;

5.4)实例化PullAPIWrapper

该类相当于生产者中的MQClientAPIImpl,专门用于处理网络发送接收相关;该类封装了MQClientInstance,而MQClientInstance中又封装了MQClientAPIImpl,所以PullAPIWrapper具备网络发送接收的功能;另外有一个实例变量pullFromWhichNodeTable被直接初始化为chm了,key表示mq,value表示brokerId,表示下次该mq从broker的主节点还是从节点拉取数据;

5.5)实例化RemoteBrokerOffsetStore

默认集群模式,会实例化该类;

5.6)实例化消费服务并启动

这里是并发消费所以实例化ConsumeMessageConcurrentlyService,若是顺序消费,则实例化ConsumeMessageOrderlyService;并启动该服务;
延迟15分钟,每15分钟执行一次清理过期消息任务,清理条件是消费者本地如果某条消息15分钟还未被消费,则需要回退该消息;从ProcessQueue中的TreeMap中的第一个元素开始循环(最多16次),调用回退方法,第二个入参是延迟级别为3;这里还有会判断若回退期间消息并没有被消费,才将该条消息从TreeMap中移除掉;向MQClientInstance实例中的消费者集合consumerTable中注册,即向该Map中存入消费者group和当前DefaultMQPushConsumerImpl实例;

5.7)将消费者实例注册进消费者报表

这里用到了观察者模式,向consumerTable表中添加group,DefaultMQPushConsumerImpl生产者实例,分别作为key和value,意味着一个消费者组只对应一个消费者实例;注意消费者实例和客户端实例不是一回事;

5.8)MQClientInstance#start方法启动客户端实例

这一步和生产者逻辑一致;重点分析与消费者相关的部分;如定时任务持久化消费进度,启动拉取消息线程,启动重平衡线程;

5.8.1)更新topicSubscriptionInfoTable

客户端每30秒更新topic路由信息的定时任务中,此时会更新RebalanceImpl中消费者订阅主题的队列信息topicSubscriptionInfoTable,key为topic,value为set;此处和5.9节中的逻辑一致;

5.8.2)客户端每隔30秒向所有broker发送心跳

包含注册信息如clientId等,其中在broker端收到心跳后会执行ConsumerManager#registerConsumer方法,会将clientId等信息存储到ConsumerManager的consumerTable中,在执行doRebalance时,会遍历当前消费者组下的所有clientId;

5.8.3)持久化消费进度

消费者每隔5秒持久化一次消费进度即offset,将内存中的偏移量存储在消费记录文件中;

5.8.4)pullMessageService.start()

开启一个消费者拉取消息线程,从pullRequestQueue.take()中拿到请求,pullMessage(pullRequest)将请求发往broker,从broker中拉取需要消费的消息,broker返回消息给消费者,消费者本地会执行回调方法pullCallBack,根据返回状态,选择不同操作,若拉取消息成功,则执行监听方法,执行监听方法之后,又会返回结果,若拉取消息失败,则将该失败消息再次发往broker,准备再次消费。拉取消息消费的具体细节在后边小节中专门展开分析;

5.8.5)rebalanceService.start()

开启重平衡组件,会立即进入超时20s的countDownLatch阻塞,被唤醒或自动醒来后,会执行doRebalance方法,接着会执行dispatchPullRequest方法,目的是构建pullRequest,并放入pullRequestQueue;负载均衡具体细节在后边小节中专门展开分析;

5.9)更新主题订阅信息topicSubscriptionInfoTable

首先遍历5.2节中的subscriptionInner表,根据当前消费者订阅的topic,从nameserver拿到对应的最新的路由数据TopicRouteData实例,与本地比较,不同则更新本地topicRouteData缓存;
接着更新brokerAddrTable;
接着将topicRouteData转为topicPublishInfo,遍历topicRouteData中的List,queueData中写队列数为多少,就创建多少个messageQueue,且编号从0开始,编号代表的就是queueId;新的queueData,编号又从0开始;本质是找到当前topic分布在哪些broker的哪些queueId上,最后将当前topic对应的messageQueue加到list集合中;
接着将topicRouteData转为subscriptionInfo,遍历topicRouteData中的List,queueData中读队列数为多少,就创建多少个messageQueue,且编号从0开始,编号代表的就是queueId;新的queueData,编号又从0开始;,最后将当前topic对应的messageQueue加到set集合中,最后更新topicSubscriptionInfoTable表;
最后调用countDownLatch的signal方法,立即唤醒重平衡rebalanceService线程;

消费总览
可以分为9大部分
1)启动前;
2)启动;
3)分配messageQueue给消费者;
4)pullMessageService线程拿到pullRequest;
5)broker端返回消息,执行callback回调函数;
6)长轮询机制;
7)消息失败重试;
8)延迟消息
9)实时唤醒重平衡线程;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

orcharddd_real

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值