RocketMQ源码分析之顺序消费

本文深入分析RocketMQ的顺序消费机制,包括Consumer如何通过分布式锁确保消息的严格顺序消费,以及在消费过程中涉及的关键步骤和处理策略,如消息队列的获取与移除、消费结果的处理等。
摘要由CSDN通过智能技术生成

概述

RocketMQ按照顺序消费有两种顺序级别,一种是普通顺序消息。另外一种是更完全严格顺序

  • 普通顺序消息指的是Producer将消息发送到相对应的消息队列上面
  • 完全严格顺序:在普通顺序消息的基础上,Consumer严格进行顺序消费
    在绝大部分情况下只需要用到普通顺序消息,大部分的应用都能容忍短暂的乱序;官方文档给出的说明中表示目前只有数据库的Binlog同步会强依赖完全严格顺序(要保证数据库事务的ACID特性)。

Comsumer严格顺序消费

Consumer在进行严格顺序消费的时候,需要利用到三把锁,Broker消息队列锁——>Consumer消息队列锁——>Consumer消息处理队列消费锁,锁粒度越来越细
- Broker消息队列锁是一个分布式锁,在集群模式下需要,在广播模式下不需要,集群模式中,只有获得该锁才能对Broker中的消息队列进行消息拉取操作
- Consumer消息队列锁是一个Broker本地锁,只有Consumer获得该锁后才能操作消息队列
- Consumer
- 消息处理队列消费锁是消费者客户端的一个本地锁,只有Consumer获得该锁后才能对消息处理队列进行消息的消费。

获取分布式Broker消息队列锁【RebalanceImpl.java#updateProcessQueueTableInRebalance】

在集群分布式模式下,Broker会不断接收消息队列的更新请求,Broker就会通过更新操作检查这个消息队列是不是属于自己,具体就是通过锁定这个分布式锁实现的,如果锁获取成功,表示这个消息队列是属于自己的,允许进行消息拉取,如果获取锁失败,则不允许进行消息拉取。
Broker消息队列分布式锁默认30s会过期,因此Consumer需要不断刷新该锁的过期时间,默认20s就会刷新一次

 1: // ⬇️⬇️⬇️【ConsumeMessageOrderlyService.java】
  2: public void start() {
   
  3:     if (MessageModel.CLUSTERING.equals(ConsumeMessageOrderlyService.this.defaultMQPushConsumerImpl.messageModel())) {
   
  4:         this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
   
  5:             @Override
  6:             public void run() {
   
  7:                 ConsumeMessageOrderlyService.this.lockMQPeriodically();
  8:             }
  9:         }, 1000 * 1, ProcessQueue.REBALANCE_LOCK_INTERVAL, TimeUnit.MILLISECONDS);
 10:     }
 11: }
移除消息队列(已有Consumer对这个消息队列消费了,移除它就是告诉Broker这个消息队列不需要其他的Consumer来消费)RebalancePushImpl.java#removeUnnecessaryMessageQueue

在集群模式下,为了避免其它Consuer在获取分布式锁时和消息队列的消费冲突,如果获取锁失败,进行移除消息队列将会失败,等到下次重新分配消费队列的时候,再进行移除;如果在没有获取分布式锁的情况下就进行消息队列移除,那么可能会导致当前Consumer和其他的Consumer同时消费该消息队列,这样将恶恶法保证消息按照完全严格顺序消费。
在解锁Broker消息队列锁的时候,如果消息队列存在剩下没被拉取的消息,则进行延迟解锁【RebanlancePushImpl.java#unlockDelay】Broker消息队列锁。因为这样能保证还没被拉取的消息能被全部拉取进行消费,这样才能保证消息被完全严格顺序消费。

消费消息队列

消费消息时序图

这里写图片描述

// ⬇️⬇️⬇️【ConsumeMessageOrderlyService.java】
  2: class ConsumeRequest implements Runnable {
  3: 
  4:     /**
  5:      * 消息处理队列
  6:      */
  7:     private final ProcessQueue processQueue;
  8:     /**
  9:      * 消息队列
 10:      */
 11:     private final MessageQueue messageQueue;
 12: 
 13:     @Override
 14:     public void run() {
 15:         if (this.processQueue.isDropped()) {
 16:             log.warn("run, the message queue not be able to consume, because it's dropped. {}", this.messageQueue);
 17:             return;
 18:         }
 19: 
 20:         // 获得 Consumer 消息队列锁
 21:         final Object objLock = messageQueueLock.fetchLockObject(this.messageQueue);
 22:         synchronized (objLock) {
 23:             // (广播模式) 或者 (集群模式 && Broker消息队列锁有效)
 24:             if (MessageModel.BROADCASTING.equals(ConsumeMessageOrderlyService.this.defaultMQPushConsumerImpl.messageModel())
 25:                 || (this.processQueue.isLocked() && !this.processQueue.i
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值