文章中如果有图看不到,可以点这里去 csdn 看看。从那边导过来的,文章太多,没法一篇篇修改好。

RocketMQ 内容详解【二、RocketMQ 机制篇(核心特性与原理)】

第二章 · RocketMQ 机制篇(核心特性与原理)

2.1 消息存储机制

2.1.1 存储架构概览

RocketMQ 的消息存储是统高吞吐和可靠性的核心。存储架构主要由三部分组成:CommitLog、ConsumeQueue 和 IndexFile。

模块功能
CommitLog消息的物理存储,顺序写磁盘,保证高吞吐
ConsumeQueue消息逻辑索引,用于 Consumer 快速定位消息
IndexFileKey 索引文件,用于根据消息 key 快速检索消息

整体架构如下:

在这里插入图片描述

每个模块的设计目标都是 高性能、高可靠和可扩展


2.1.2 CommitLog 设计

CommitLog 消息写入流程

在这里插入图片描述
源码解析

  1. DefaultMQProducer.send() 调用 sendKernelImpl()
  2. sendKernelImpl() 会查询 Topic 路由信息 (tryToFindTopicPublishInfo)
  3. Broker 的 CommitLog.appendMessage() 顺序写入文件
  4. ConsumeQueue 使用 逻辑队列映射 CommitLog 物理偏移
  5. IndexFile 根据消息 Key 建立倒排索引
  6. Producer 收到 ACK 后完成发送流程
顺序写磁盘
  • CommitLog 使用 MappedFile 映射磁盘文件,每个文件默认 1G
  • 消息顺序追加写入,避免随机磁盘 I/O
  • 每条消息使用 AppendMessageCallback 进行序列化后写入
零拷贝机制
  • 使用 FileChannel.transferTo() 实现从内存映射文件到网络缓冲区的零拷贝
  • 数据在内存和网络之间无需多次复制,提高发送吞吐
消息结构(源码 MessageExtEncoder)
int totalSize;        // 消息总长度
int magicCode;        // 魔数校验
int bodyCRC;          // 消息体CRC校验
int queueId;          // 队列ID
long queueOffset;     // 队列偏移
long physicalOffset;  // 物理偏移
long sysFlag;         // 系统标记
long bornTimestamp;   // 消息生成时间
long storeTimestamp;  // 存储时间
int bodyLength;       // 消息体长度
byte[] body;          // 消息体
刷盘策略
  • 同步刷盘(SYNC_FLUSH):Producer 等待 Broker 写入磁盘确认
  • 异步刷盘(ASYNC_FLUSH):Broker 异步写磁盘,提高吞吐,但可能丢失最后几条消息

2.1.3 ConsumeQueue 设计

  • 逻辑队列:每个 Topic + Queue 对应一个 ConsumeQueue 文件
  • 索引结构:每条索引 20 字节,包括物理偏移、消息长度和 tagCode
  • 作用:Consumer 通过 ConsumeQueue 查找消息在 CommitLog 的偏移,然后拉取消息
  • 文件结构:顺序追加,每条索引固定长度,支持快速二分查找
ConsumerBrokerConsumeQueueCommitLogpullMessage(topic, queueId, offset)lookupMessage(offset, batchSize)messageIndexListreadMessage(physicalOffset)MessageExt返回消息列表ackMessage(msgId)ConsumerBrokerConsumeQueueCommitLog

源码解析

  • Consumer 可以是 Push 模式或 Pull 模式,本质仍是 Pull 拉取
  • Broker 通过 ConsumeQueue 快速找到消息物理偏移
  • 然后从 CommitLog 中读取消息内容
  • 消费成功后,Consumer 调用 ackMessage 更新消费进度

源码片段(存储索引)示意:

public boolean putMessagePositionInfoWrapper(final long offset, final int size, final long tagsCode, final long storeTimestamp, final long bitMap) {
    // 生成索引条目
    // 写入逻辑队列文件
}

2.1.4 IndexFile 设计

  • Key 索引:支持根据业务 key 快速检索消息
  • 文件结构:Hash(key) → 链表 → CommitLog 偏移
  • 索引更新:每条消息写入 CommitLog 时同步更新 IndexFile

源码关键类:IndexService,提供查询接口 queryOffset(topic, key)


2.2 消息发送与消费流程

2.2.1 消息发送方式

RocketMQ 支持三种发送模式:

模式特点使用场景
同步发送Producer 等待 Broker ACK高可靠场景,如支付消息
异步发送Producer 通过回调接收 ACK高吞吐场景,如日志收集
单向发送Producer 不等待 ACK高性能但不可靠,如统计数据

源码调用链:

public SendResult send(final Message msg) throws MQClientException, RemotingException {
    TopicPublishInfo publishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
    MessageQueue mq = selectOneMessageQueue(publishInfo);
    sendKernelImpl(msg, mq, communicationMode, sendCallback);
}

2.2.2 消费模式:Push / Pull

  • Pull 模式:Consumer 主动拉取消息,可以控制消费速率和并发
  • Push 模式:Broker 将消息“推送”给 Consumer,本质上仍是 Pull + 长轮询

消息流序列图:

ProducerBrokerConsumer发送消息ACK拉取消息返回消息更新消费进度ProducerBrokerConsumer

2.2.3 消息重试与幂等性

  • 重试机制:消费失败消息进入重试队列,消费端需要幂等处理
  • 重试次数配置maxReconsumeTimes
  • 延迟队列:重试消息根据延迟等级延迟重新投递

2.3 消息顺序与事务消息

2.3.1 顺序消息实现原理

  • 局部顺序:Queue 级别顺序保证
  • 线程绑定:Consumer 每个线程消费一个 Queue
ProducerBrokerConsumersend(msg, queueId = hash(key) % N)按队列顺序分配Consumer线程ackMessageProducerBrokerConsumer

源码解析

  • Producer 通过业务 Key 计算 QueueId,保证同一业务 Key 消息发送到同一队列

  • Broker 保证顺序写 CommitLog 与 ConsumeQueue

  • Consumer 同线程顺序消费队列消息

  • 源码示意

int queueId = Math.abs(key.hashCode()) % topicPublishInfo.getMessageQueueList().size();
send(msg, queueId);

2.3.2 事务消息(半消息机制)

  • 半消息:Producer 发送事务消息到 Broker,消费者不可见
  • 事务回查:Broker 定期检查未提交的事务消息,调用 Producer 回查接口
  • 源码流程
ProducerBrokerTransactionManagerConsumersendHalfMessage(msg)ackHalfMessageexecuteLocalTransaction(msg)transactionResult(COMMIT / ROLLBACK)commitOrRollback(msgId)可见消息 (COMMIT)checkTransactionState()commit/rollbackalt[事务未回查]ProducerBrokerTransactionManagerConsumer

源码解析

  1. sendMessageInTransaction() 发送半消息到 Broker
  2. Broker 将消息标记为 HALF,暂不对 Consumer 可见
  3. Producer 执行本地事务逻辑
  4. TransactionManager 根据执行结果通知 Broker 提交或回滚
  5. Broker 定期回查未提交事务,确保消息最终一致性
sendMessage(MessageType.HALF_MSG)
checkTransactionState(HALF_MSG)
  • 提交或回滚:Producer 根据业务执行结果调用 commit 或 rollback

2.4 消息过滤与路由机制

ConsumerNameServerBrokerConsumeQueue查询Topic路由信息返回Broker列表与Queue信息拉取消息消息索引过滤(Tag / SQL92)匹配消息返回消费消息ConsumerNameServerBrokerConsumeQueue

源码解析

  1. Consumer 通过 NameServer 获取路由信息
  2. Broker 根据 ConsumeQueue 中 tagsCode 或 SQL92 表达式过滤消息
  3. Consumer 只接收匹配的消息

2.4.1 Tag 与 SQL92 过滤

  • Tag:轻量级过滤,可在 Broker 端快速匹配
  • SQL92:复杂过滤,支持消息属性表达式
  • Broker 可在 ConsumeQueue 先做 Tag 过滤,再在 Consumer 做 SQL92 过滤

2.4.2 NameServer 与路由发现

  • 轻量级服务发现,维护 Topic 与 Broker 的映射
  • Producer / Consumer 查询路由
public TopicRouteData getRouteInfoFromNameServer(String topic)
  • 支持 Broker 动态上下线,Producer 路由自动更新

2.5 消息重试与死信队列(DLQ)

超过maxReconsumeTimes
Consumer消费失败
延迟队列Level 1
延迟队列Level 2
死信队列
重新消费

源码解析

  • Broker 内部维护多个延迟队列
  • 消费失败消息根据 delayLevel 放入相应队列
  • Consumer 重试拉取
  • 超过 maxReconsumeTimes 的消息进入 DLQ,便于人工处理或补偿

2.5.1 重试策略

  • 消费失败消息进入重试队列,延迟投递

  • 延迟队列

    • 延迟等级 1~18 对应不同时间间隔
    • Broker 根据 delayLevel 调整消息投递时间
  • 源码逻辑:

int delayLevel = computeDelayLevel(reconsumeTimes);
putMessageToDelayQueue(msg, delayLevel);

2.5.2 DLQ 存储与处理

  • 超过最大重试次数消息进入死信队列(DLQ)
  • Consumer 可定期扫描 DLQ 进行补偿处理
超过重试次数
消费失败
延迟队列
死信队列
重新消费

2.6 总结

  • RocketMQ 存储机制:CommitLog 顺序写 + 零拷贝、ConsumeQueue 逻辑索引、IndexFile Key 索引
  • 消息发送与消费:同步/异步/单向发送,Push/Pull 消费模式,重试与幂等性
  • 顺序与事务消息:Queue 级顺序保证,半消息 + 回查机制实现事务消息
  • 消息过滤与路由:Tag/SQL92 过滤,NameServer 路由发现
  • 重试与死信队列:延迟队列 + DLQ 保障可靠性

本章内容为源码级、机制原理级全面解析,为深入理解 RocketMQ 消息传输、存储和消费流程奠定基础。

posted @ 2025-09-18 15:55  NeoLshu  阅读(5)  评论(0)    收藏  举报  来源