消费者启动
消息拉取
集群模式:各个消费者通过负载均衡的方式消费消息
广播模式:每一个消费者消费所有消息
消息队列负载与重新分布机制
消息消费过程
PullMessageService负责对消息队列进行消息拉取,从远端服务器拉取消息后将消息存储ProcessQueue消息队列处理队列中,然后调用ConsumeMessageService#submitConsumeRequest方法进行消息消费,使用线程池来消费消息,确保了消息拉取与消息消费的解耦。
消息发送时数据在ConsumeQueue的落地
连续发送5条消息,消息是不定长,首先所有信息先放入 Commitlog中,每一条消息放入Commitlog的时候都需要上锁,确保顺序的写入。
当Commitlog写成功了之后。数据通过ReputMessageService类定时同步到ConsunmeQueue中,写入Consume Queue的内容是定长的,固定是20个Bytes(offset 8个、size 4个、Hashcode of Tag 8个)。
查找消息的时候,可以直按根据队列的消息序号,计算出索引的全局位置(比如序号2,就知道偏移量是20),然后直接读取这条索引,再根据索引中记录的消息的全局位置,找到消息。这两次查找是差不多的:第一次在通过序号在consumer Queue中获取数据的时间复杂度是O(1),第二次查找commitlog文件的时间复杂度也是O(1),所以消费时查找数据的时间复杂度也是O(1)。
消息消费参考:https://blog.csdn.net/yuanshangshenghuo/article/details/110494587
messagequeue和processqueue的区别
MessageQueue 和 ProcessQueue 是 RocketMQ 中的两个重要概念,它们在消息处理过程中起着不同的作用。
MessageQueue:这是 RocketMQ 的基本组成部分,用于表示拉取回来的消息元数据信息,包括 topic、brokerName、queueId 等。MessageQueue 是 RocketMQ 消息收发模型中的一个组成部分,虽然所有的消息资源以主题粒度管理,但实际的操作实现是面向 MessageQueue。
ProcessQueue:这是 RocketMQ 客户端用于处理消息的一个重要组件。ProcessQueue 是 MessageQueue 的消费快照。消息拉取的时候,会将实际消息体、拉取相关的操作存放在其中。比如消费进度,消费等等功能的底层核心数据保存都是有 ProcessQueue 提供。ProcessQueue 中包含了一些重要的成员变量,如 msgTreeMap(临时存放消息用的),msgCount(消息总数量),msgSize(整个 ProcessQueue 处理单元的总消息长度)等。
消息消费时,messagequeue锁的意义和processqueue锁的意义
在 RocketMQ 中,MessageQueue 锁和 ProcessQueue 锁在消息消费时起着不同的作用。
MessageQueue 锁:这是一个全局锁,用于在 ConsumeMessageOrderlyService 中对整个 MessageQueue 进行加锁。这样做的目的是保证同一时刻只有一个线程对 MessageQueue 进行处理。这个锁是在 ConsumeRequest.run() 方法中实现的。如果获取到这个 messageQueue 的锁,则进行消息消费;如果没有获取到锁,把该请求重新放到线程池中,该线程不会阻塞,可以继续处理线程池中的其他请求。另外,这个锁还有另一个作用,那就是防止在当前消费者还在消费,且没有提交位移时发生了重平衡,正在消费的 messagequeue 被分配给了其他消费者,导致重复消费。
ProcessQueue 锁:这是一个队列级别的锁,用于保证在处理单个队列中的消息时的线程安全。当拿到锁的线程从 ProcessQueue 拿出消息时,是按照 offset 顺序拿的,从而保证按顺序处理消息。如果业务处理失败,会把当前处理的消息重新放回 ProcessQueue 中,当前线程直接结束,后续重新启动线程对消息进行处理。所以即使业务处理失败,也能保证按消息的顺序进行处理。
消息消费为什么要对message加锁
在 RocketMQ 中,对 MessageQueue 加锁的主要目的是为了保证消息消费的有序性。这是因为 RocketMQ 支持顺序消息消费,即消息需要按照发送的顺序进行消费。
当 MessageQueue 的队列个数小于消费组中消费者的个数时,如果不加锁,可能会导致多个消费者消费同一个队列,存在竞争。但是,通过查看 AllocateMessageQueueStrategy 的实现源码,发现当 MessageQueue 的队列个数小于消费组中消费者的个数时,会导致超出个数的消费者无队列可消费,即负载不均衡,但并不会出现多个消费者争抢消费同一个消费队列的情况。
另外,MessageQueue 的锁还有另一个作用,那就是防止在当前消费者还在消费,且没有提交位移时发生了重平衡,正在消费的 messagequeue 被分配给了其他消费者,导致重复消费。