携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情
之前分析过RocketMQ的Broker拉取消费如何实现,再来分析下Consumer消费的核心实现方式。
理解RocketMQ的概念与原理,需要通过看源代码和画图的方式梳理流程。
通过查看源码梳理出的Consumer消费整体流程为:拉取消息->执行消费逻辑->移除过期队列->获取锁->获取消息->消费消息->更新进度
,其中还有定时任务刷新锁、未获取锁时执行延迟获取锁、重平衡逻辑等等。
以下分步骤分析源码与画图梳理流程,这篇进行移除过期队列->获取锁
的分析
移除过期队列
更新队列负载均衡,去除获取不到锁的队列
流程图
整个逻辑的流程图如下:
代码
查询mq
是否再processQueueTable
中,在processQueueTable
中的队列尝试获取锁,获取不到锁直接不进行下面的处理。
private boolean updateProcessQueueTableInRebalance(final String topic, final Set<MessageQueue> mqSet, final boolean isOrder) {
// ..... 此处省略部分代码
// 增加 不在processQueueTable && 存在于mqSet 里的消息队列。
List<PullRequest> pullRequestList = new ArrayList<>(); // 拉消息请求数组
for (MessageQueue mq : mqSet) {
if (!this.processQueueTable.containsKey(mq)) {
if (isOrder && !this.lock(mq)) { // 顺序消息锁定消息队列
log.warn("doRebalance, {}, add a new mq failed, {}, because lock failed", consumerGroup, mq);
continue;
}
// ..... 此处省略部分代码
}
}
// ..... 此处省略部分代码
}
复制代码
锁相关代码
RocketMQ
集群模式下,每个queue
只有对应group
下的一个Consumer
获取,通过锁机制实现。
流程图
消费前会尝试获取锁,获取成功执行消费,消费结束处理消费结果、释放锁。获取失败会进入重试阶段,流程图如下:
代码
以下几个跟锁操作相关的代码。
扫描二维码关注公众号,回复:
14438198 查看本文章

获取锁
请求Broker获得指定消息队列的分布式锁。 首先获取Broker
地址,this.mQClientFactory.getMQClientAPIImpl().lockBatchMQ
根据Broker
地址批量获取持有锁MQ。
/**
* 请求Broker获得指定消息队列的分布式锁
*
* @param mq 队列
* @return 是否成功
*/
public boolean lock(final MessageQueue mq) {
FindBrokerResult findBrokerResult = this.mQClientFactory.findBrokerAddressInSubscribe(mq.getBrokerName(), MixAll.MASTER_ID, true);
if (findBrokerResult != null) {
...
Set<MessageQueue> lockedMq =
this.mQClientFactory.getMQClientAPIImpl().lockBatchMQ(findBrokerResult.getBrokerAddr(), requestBody, 1000);
for (MessageQueue mmqq : lockedMq) {
ProcessQueue processQueue = this.processQueueTable.get(mmqq);
if (processQueue != null) {
processQueue.setLocked(true);
processQueue.setLastLockTimestamp(System.currentTimeMillis());
}
}
}
return false;
}
复制代码
消费消息前,获取锁
messageQueueLock.fetchLockObject(this.messageQueue)
复制代码
定时刷新锁
定时轮询,将锁过期。Consumer
定时向Broker
刷新锁过期时间
public void start() {
if (MessageModel.CLUSTERING.equals(ConsumeMessageOrderlyService.this.defaultMQPushConsumerImpl.messageModel())) {
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
ConsumeMessageOrderlyService.this.lockMQPeriodically();
}
}, 1000 * 1, ProcessQueue.REBALANCE_LOCK_INTERVAL, TimeUnit.MILLISECONDS);
}
}
复制代码
释放锁
移除队列时,释放锁
public boolean removeUnnecessaryMessageQueue(MessageQueue mq, ProcessQueue pq) {
// 同步进度
this.defaultMQPushConsumerImpl.getOffsetStore().persist(mq);
// 移除队列
this.defaultMQPushConsumerImpl.getOffsetStore().removeOffset(mq);
if (this.defaultMQPushConsumerImpl.isConsumeOrderly()
&& MessageModel.CLUSTERING.equals(this.defaultMQPushConsumerImpl.messageModel())) {
...
if (pq.getConsumeLock().tryLock(1000, TimeUnit.MILLISECONDS)) {
try {
return this.unlockDelay(mq, pq);
} finally {
pq.getConsumeLock().unlock();
}
} else {
pq.incTryUnlockTimes();
}
...
return false;
}
return true;
}
复制代码