Skip to content

整体架构

This content is not available in your language yet.

RocketMQ 将物理写入、逻辑索引、协议处理和客户端调度拆成可独立演进的层。

先给答案:RocketMQ 先写唯一事实日志,再异步派生消费视图

Section titled “先给答案:RocketMQ 先写唯一事实日志,再异步派生消费视图”

生产请求先追加到 CommitLog,ConsumeQueue 和 IndexFile 由 Reput 后续构建;消费则先用逻辑索引定位物理 offset,再回到 CommitLog 读取消息。这把多 Topic 的随机写收敛为顺序追加,也让索引可以在故障后重建。

代价是“写入成功”和“消费可见”存在阶段差:CommitLog 已完成不代表 ConsumeQueue 已追平,复制成功也不等于客户端一定收到响应。排障必须分别观察物理写入、派生索引、复制提交和客户端重试,不能用单一 offset 推断全链路状态。

Producer / Consumer
│ Remoting command
▼
Broker: PullMessageProcessor / EndTransactionProcessor
│
▼
Store: DefaultMessageStore
├─ CommitLog -> MappedFileQueue -> mmap 文件
├─ ReputMessageService -> ConsumeQueue / IndexFile
└─ HA / DLedger -> 副本日志
层 职责 关键源码坐标
Client 生产、拉取、重平衡、位点管理 client/.../RebalanceImpl.java:232
Broker 校验请求、挂起拉取、事务状态机 broker/.../PullMessageProcessor.java:304
Store 追加写、索引、刷盘、恢复 store/.../DefaultMessageStore.java:645
HA 同步复制或 DLedger 日志 store/.../DefaultHAService.java:107
Producer -> DefaultMessageStore#asyncPutMessage
-> CommitLog#asyncPutMessage
-> commit / flush
-> ReputMessageService
├─ ConsumeQueue
└─ IndexFile

DefaultMessageStore#asyncPutMessage(DefaultMessageStore.java:645)执行写前 Hook 并交给 CommitLog;同步 API putMessage(:716)只是等待异步结果。索引由 ReputMessageService(:2655)异步推进。

Consumer -> PullMessageProcessor
├─有消息 -> ConsumeQueue -> CommitLog -> response
└─无消息 -> PullRequestHoldService
▲
NotifyMessageArrivingListener ─┘

DefaultMessageStore#getMessage(DefaultMessageStore.java:865)先取物理偏移,再由 CommitLog#getMessage(CommitLog.java:1439)读取消息体。无消息时 PullMessageProcessor#processRequest(PullMessageProcessor.java:304)把请求交给长轮询。

为什么采用“日志 + 派生索引”

Section titled “为什么采用“日志 + 派生索引””

替代方案:每个 Topic/Queue 独立文件并在写入时同步更新。 为什么不行:多 Topic 并发写会放大随机 IO、刷盘协调和恢复成本。 证据:DefaultMessageStore 注册 CommitLogDispatcherBuildConsumeQueue(:248)和 CommitLogDispatcherBuildIndex(:249),索引明确位于物理日志之后。

场景 现象 原因 规避
物理日志已写、索引未追上 短时查不到消息 Reput 异步派生 监控 dispatch lag
角色切换 写入暂时失败 新主尚未完成状态切换 客户端重试
索引损坏 消费位点异常 派生文件不是事实源 从 CommitLog 重建

“事实日志 + 可重建索引”适合搜索、事件溯源和 CDC:先保证事实追加顺序,再用消费者投影出不同查询模型。

面试锚点

  • 为什么 RocketMQ 不直接按消费队列写文件?
  • Reput 延迟会造成什么可见性现象?
  • 消费请求如何从逻辑偏移回到物理消息?