RocketMQ源码学习 (十四) 顺序消费服务原理

1 引文

之前的内容里学习了rocketMQ的消费者并发消费的相关原理和源码,本篇文章开始学习顺序消费的相关原理。
首先,什么是顺序消费?,在一个queue里面,消息需要按照FIFO的规则,RocketMQ不支持全局顺序消费,只支持在一个主题下的一个queue队列是有序的,如果想要做到全局顺序消费该怎么办呢?设置只有一个queue就可以了,但是这样性能很低。并发消费不能支持顺序消费,并发消费中Rebalance分配队列给当前消费者,这个阶段分配的队列不能保证同时只有该消费者被消费,几个消费者同时消费一个队列就不能讲顺序这件事,要保证顺序消费,就必须保证一个队列在全局内同一时刻只能有一个消费者在消费。
RocketMQ的顺序消费的前提:

  1. queue必须加锁,加在broker的分布式锁,在broker中有个 Map<group@topic, Map<int(queue), Entry>>,这里面的Entry就是锁记录,记录了这把锁的归属,续约时间,当获取到锁的时间就是续约时间,如果客户端续约时间在60s以内则还持有该锁,超过60s则其他消费者就可以拿到这把锁了;
  2. 消费者本地必须保证同一时间只能有一个消费任务(一个消费任务就是对应一个线程)在执行消费任务。
    在说完这个前提之后,就开启本文的学习之旅。

2 doRebalance的逻辑

在下图中,如果PQ(ProcessQueue)获取到锁成功,如果当时PQ的treeMap为Empty,不会延迟20秒,直接释放锁,如果不是Empty则会延迟20秒再释放锁,为什么要延迟20秒呢?保证全局范围内同时只能有一个消费任务,如果立刻释放锁,消费任务还没完全退出任务,其他节点有可能拿到这把锁,这样全局就会有两个消费任务去消费这个queue。
顺序消费和并发消费不一样,顺序消费是从服务器pull下来后将msg存到msgTreeMap,存的时候会检查PQ的consuming的状态是否是消费中,为false说明消费者这里没有消费任务在运行,将这条msg添加进去之后会将其改成true,并且会向外返回一个bool值(和consuming一致),顺序消费服务会根据这个返回值去创建顺序消费任务提交到线程池去执行。顺序消费提交线城池是不包含消息的,需要通过takeMessages方法区拿消息。
对于这些新分配给当前消费者该主题的队列,需要做的事情中,除了第0步之外和并发消费并无差别
在这里插入图片描述

3 ProcessQueue 的一些处理

ProcessQueue 消费者本地队列的相关处理流程如下,这些方法会在顺序消费中被调用,这里需要注意takemessage方法。
ProcessQueue 消费者本地队列快照

4 顺序消费服务

顺序消费原理图如下,顺序消费服务启动时会向任务调度线程提交锁续约任务,每20秒向broker续约一次,lockMQPeriodically()方法调用rebalancelmpl#lockAlI()方法,对消费者占用的队列锁进行续约

在这里插入图片描述
在RebalanceImpl中的lockAlI方法如下图,查看锁续约时间是否过期,更新续约成功的processQueue属性中,locked设置为true,lastLockTimestamp设置为当前时间,如果没有过期说明哈hi有任务再执行,则续约失败
在这里插入图片描述
顺序服务在启动阶段提交锁续约任务执行方法之后开始提交消费任务请求,即submitConsumeReqest(…)方法,该方法的参数主要有:

参数说明
msgFoundListBroker 拉取下来的该messageQueue的消息
processQueueConsumer队列快照
messageQueue
dispatchToConsume顺序消费根据该参数控制是否提交新的消费任务

创建新的消费任务需要创建ConsumerRequest,其组成如下图,,里面有核心消费任务的程序入口,顺序消费逻辑必须保证在消费者端同一时刻只能有一个线程在执行。

在这里插入图片描述

上图中顺序消费服务的本地队列锁如下图所示,保证了同一个queue只有一个任务再执行,本地为什么要获取objLock的锁?咱们不是已经获取了Broker端该Queue的独占锁了?
原因: broker queue占用锁的角度是client占用,client从broker的某个占用了锁的queue拉取下来消息之后,将消息存储到了消费者本地的processQueue中。processQueue快照对象内有个属性叫做"volatile boolean consuming" ,表示本地
的队列是否正在被消费处理中…processQueue.takeMessage(.)方法,到processQueue获取下一批次"待处理消
息”,获取到了的话run0方法就处理,获取不到会修改processQueue.consuming =false,本消费任务马上就终止,但是从代码角度去看还有一 些代码要跑…
此时,如果恰巧pull再次拉回来一 批当前processQueue的msg, 会再次向ConsumeMessageOrderlyService提交消费任务, 所以需要objLock去同步本地的线程。

在这里插入图片描述

任务执行的逻辑如下:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值