RockerMQ之commitlog与consumequeue

上回说到了commitlog与consumequeue,那么他们之前是怎么联系的呢,这就是咱们这篇的主题了,当然之前写的文章如果有任何问题,欢迎大家批评指教;写完这个之后还是的先写索引,不过已经这么写了就这样吧,后面补上,大手空中一挥:欠一篇

复制代码
┌─────────────────────────────────────────  写流程  ─────────────────────────────────────────┐
│                                                                                            │
│  Producer  →  发送消息  →  Broker  →  写入 CommitLog  →  异步线程 ReputMessageService  →  构建 ConsumeQueue  │
│                                                                                            │
└────────────────────────────────────────────────────────────────────────────────────────────┘
                                                                                                ↓
┌─────────────────────────────────────────  读流程  ─────────────────────────────────────────┐
│                                                                                            │
│  Consumer  →  请求 Topic-QueueId 消息  →  Broker  →  读取 ConsumeQueue 索引  →  定位 CommitLog  →  返回消息  │
│                                                                                            │
└────────────────────────────────────────────────────────────────────────────────────────────┘

写流程

复制代码
┌───────────────────────────────────────────────────────────────────┐
│  Step 1:消息写入 CommitLog 成功                                   │
│         (生成全局唯一物理偏移量、消息长度、Tag哈希值)              │
│                                                                   │
│  Step 2:ReputMessageService 监听 CommitLog 新消息                 │
│         (从 CommitLog 中解析出 Topic、QueueId、Tag 等信息)         │
│                                                                   │
│  Step 3:构建 20 字节 ConsumeQueue 索引项                          │
│         ┌──────────┬──────────┬──────────┐                        │
│         │物理偏移量│  消息长度 │  标签哈希 │  ←  写入 ConsumeQueue  │
│         │  8字节   │  4字节   │  8字节   │                        │
│         └──────────┴──────────┴──────────┘                        │
│                                                                   │
│  Step 4:索引项追加写入对应 Topic-QueueId 的 ConsumeQueue 文件      │
│         →  文件未满:直接追加到末尾                                │
│         →  文件已满:自动新建文件(文件名=当前逻辑偏移量)           │
│                                                                   │
│  Step 5:更新 ConsumeQueue 逻辑偏移量(记录下一个索引项位置)        │
└───────────────────────────────────────────────────────────────────┘

咱们看到了ReputMessageService监听commitlog,说到监听,这个词本身就有点异步的味道,像警察叔叔蹲守XX,那不可能一天24小时面对面一直盯着跟着,那能抓到吗?是不是,咱们ReputMessageService异步构建索引,然后后面的工作人员再悄悄地写入,而消息你到达commitlog了之后你就可以转脸向producer邀功,让她看看你有多快!我ReputMessageService来将消息真正的落索引生根,否则同步到磁盘这io、重操作大家都干等着太熬人了 是吧,而且这样一来异步也可以等攒一波了再刷盘,这样效率是不是更高了,处处都是智慧,真厉害。

这个地方有点重要,咱们再细说一下:

复制代码
┌───────────────────────────────────────────────────────────────────┐
│                        启动阶段                                     │
│  ┌─────────────┐    ┌─────────────┐    ┌────────────────────────┐  │
│  │ Broker 启动 │ →  │ 初始化线程  │ →  │ 设置 reputFromOffset   │  │
│  │             │    │  并启动     │    │  为 CommitLog 最大偏移 │  │
│  └─────────────┘    └─────────────┘    └────────────────────────┘  │
└───────────────────────────┬─────────────────────────────────────────┘
                            ↓
┌───────────────────────────────────────────────────────────────────┐
│                        循环工作阶段(核心)                          │
│                                                                   │
│  ┌─────────────────────────────────────────────────────────────┐  │
│  │  Step 1:检查线程运行状态(isRunning 是否为 true)           │  │
│  │          ├─ 否 → 退出循环,线程终止                          │  │
│  │          └─ 是 → 进入下一步                                  │  │
│  └─────────────────────────────────────────────────────────────┘  │
│                                                                   │
│  ┌─────────────────────────────────────────────────────────────┐  │
│  │  Step 2:获取 CommitLog 最新写入偏移量(maxPhyOffset)       │  │
│  │          ├─ reputFromOffset ≥ maxPhyOffset → 无新消息        │  │
│  │          │   └─ 休眠 sleepTimeInMillis,跳回 Step 1          │  │
│  │          └─ reputFromOffset < maxPhyOffset → 有新消息        │  │
│  └─────────────────────────────────────────────────────────────┘  │
│                                                                   │
│  ┌─────────────────────────────────────────────────────────────┐  │
│  │  Step 3:从 reputFromOffset 读取 CommitLog 消息              │  │
│  │          ├─ 解析消息:Topic、QueueId、Tag、消息长度、物理偏移 │  │
│  │          ├─ 校验消息合法性(Magic Code + CRC32)             │  │
│  │          └─ 校验失败 → 跳过此消息,reputFromOffset 后移      │  │
│  └─────────────────────────────────────────────────────────────┘  │
│                                                                   │
│  ┌─────────────────────────────────────────────────────────────┐  │
│  │  Step 4:构建 ConsumeQueue 索引项(20字节)                  │  │
│  │          ┌──────────┬──────────┬──────────┐                  │  │
│  │          │物理偏移量│  消息长度 │  标签哈希 │                  │  │
│  │          └──────────┴──────────┴──────────┘                  │  │
│  │          ├─ 写入对应 Topic-QueueId 的 ConsumeQueue 文件       │  │
│  │          └─ 更新 ConsumeQueue 逻辑偏移量                     │  │
│  └─────────────────────────────────────────────────────────────┘  │
│                                                                   │
│  ┌─────────────────────────────────────────────────────────────┐  │
│  │  Step 5:构建 IndexFile 索引(可选,按 Key 索引)            │  │
│  │          ├─ 判断消息是否带 Key(如 messageKey)               │  │
│  │          ├─ 是 → 计算 Key 哈希,写入 IndexFile 索引          │  │
│  │          └─ 否 → 跳过此步骤                                  │  │
│  └─────────────────────────────────────────────────────────────┘  │
│                                                                   │
│  ┌─────────────────────────────────────────────────────────────┐  │
│  │  Step 6:更新 reputFromOffset 到下一条消息的物理偏移         │  │
│  │          └─ 跳回 Step 1,继续循环处理                        │  │
│  └─────────────────────────────────────────────────────────────┘  │
└───────────────────────────────────────────────────────────────────┘
                            ↓
┌───────────────────────────────────────────────────────────────────┐
│                        终止阶段                                     │
│  ┌─────────────┐    ┌─────────────┐    ┌────────────────────────┐  │
│  │ Broker 关闭 │ →  │ 设置 isRunning=false │ →  线程退出循环    │  │
│  │             │    │                │    │  资源释放(内存映射) │  │
│  └─────────────┘    └─────────────┘    └────────────────────────┘  │
└───────────────────────────────────────────────────────────────────┘

诶,咱们这是不是多看到了reputFromOffset,这很重要:ReputMessageService 线程通过 reputFromOffset 检测到 CommitLog 有新消息,读取并解析消息的 Topic、QueueId、Tag 等信息,异步构建 ConsumeQueue 索引项!

当然这个过程还有很多细节要说

不过呢先到这里吧,太多了也不容易理解;

读流程

相对来说,读就简单了,人家要消费那就是上帝,怎么能让上帝感觉麻烦呢是不是,那有点不懂事了:

复制代码
┌───────────────────────────────────────────────────────────────────┐
│  Step 1:Consumer 向 Broker 发送拉取请求                          │
│         (携带参数:Topic、QueueId、消费偏移量 offset)              │
│                                                                   │
│  Step 2:Broker 根据 offset 定位 ConsumeQueue 文件                  │
│         →  计算文件位置:offset ÷ 单文件最大索引项数                │
│         →  计算文件内位置:offset % 单文件最大索引项数               │
│                                                                   │
│  Step 3:读取 20 字节索引项,解析关键信息                          │
│         ┌──────────┬──────────┬──────────┐                        │
│         │物理偏移量│  消息长度 │  标签哈希 │  ←  读取并解析         │
│         │  8字节   │  4字节   │  8字节   │                        │
│         └──────────┴──────────┴──────────┘                        │
│                                                                   │
│  Step 4:Tag 过滤(可选)                                          │
│         →  Consumer 指定 Tag:对比索引项 Tag 哈希值,不匹配则跳过    │
│         →  未指定 Tag:直接进入下一步                              │
│                                                                   │
│  Step 5:根据物理偏移量+消息长度,从 CommitLog 读取完整消息          │
│                                                                   │
│  Step 6:Broker 将消息返回给 Consumer,更新消费进度                  │
└───────────────────────────────────────────────────────────────────┘

我们看到consumer同步拉取咱们的索引,定位到commitlog,借助索引的力量避免全量扫描;

全流程

下面很详细,我就不说了,适当的退场是对思考的无声的尊重。

复制代码
┌─────────────────┐        ┌─────────────────────────┐        ┌─────────────────┐
│  Producer 线程  │        │        Broker 进程       │        │  Consumer 线程  │
└────────┬────────┘        └───────────┬─────────────┘        └────────┬────────┘
         │                             │                               │
         │ 1. 发送消息到 Broker         │                                │
         │─────────────────────────────>                               │
         │                             │                               │
         │                             │  ┌─────────────────────────┐  │
         │                             │  │  CommitLog 写入线程 合法性校验    │ │
         │                             │  │  - 顺序写入 CommitLog 内存 生成偏移量长度等元数据  │ │
         │                             │  └───────────┬─────────────┘  │
         │                             │              │                │
         │                             │  ┌───────────▼─────────────┐  │
         │                             │  │ FlushCommitLogService    │ │
         │                             │  │  - 异步刷盘 CommitLog  批量  │ │
         │                             │  └───────────┬─────────────┘  │
         │                             │              │                │
         │                             │  ┌───────────▼─────────────┐  │
         │                             │  │ ReputMessageService      │ │
         │                             │  │  -拿着reputfromoffset 监听 CommitLog 新消息 │ │
         │                             │  │  - 解析消息topic\queueid\tag异步构建 ConsumeQueue 索引│ │
         │                             │  └───────────┬─────────────┘  │
         │                             │              │                │
         │                             │  ┌───────────▼─────────────┐  │
         │                             │  │FlushConsumeQueueService  │ │
         │                             │  │  - 异步刷盘 ConsumeQueue │  │
         │                             │  └───────────┬─────────────┘  │
         │                             │              │                │
         │                             │  ┌───────────▼─────────────┐  │
         │                             │  │ PullMessageProcessor     │ │
         │  3. 返回消息给 Consumer     │  │  - 接收 Consumer 拉取请求│ │  2. 拉取消息
         │<────────────────────────────┼──│  - 读 ConsumeQueue 索引   │◄───────────
         │                             │  │  - 定位 CommitLog 消息   │ │
         │                             │  └─────────────────────────┘ │
         │                             │                              │
         │                             │                              │

赠送:

因为咱们rocketmq的commitlog与consumequeue的精心设计,他们之间若隐若离,看似没有关系很解耦合,但是又剪不断千丝万缕的情丝,甚至当ConsumeQueue 损坏 ,ReputMessageService 可以从头遍历 CommitLog,重构 ConsumeQueue 索引,因为consumequeue是不是20个字节,就那三个东西,你说哪一个咱们commitlog没有!都有所以你坏了,读不了了,咱也不需要依赖原有索引文件,是不是很坚强!

咱们的ReputMessageService本身也很贴心,不仅是对别人更是对自己:比如当commitlog写入过快,ReputMessageService焦头烂额的时候,他就给自己放假:自动调整休眠时间,忙不过来明显是睡得不够,需要多休息,这样也释放了cpu,把资源留给更需要的commitlog,这样二全齐美、一箭双雕,不得不为咱们ReputMessageService点赞,就是不知道他这种思维和工作方式怎么让我的老板知道「🤔️」

不过多休息也不行了,当ReputMessageService不堪重负直接瓜了,怎么办?没关系、咱们rocketmq能想不到吗,还有broker这个总指挥大领导,当 ReputMessageService 线程异常挂掉 ,Broker 会自动重启该线程,并从 reputFromOffset 断点继续处理,所以就不会重复或遗漏索引构建。

相关推荐
天天摸鱼的java工程师16 小时前
工作中 Java 程序员如何集成 AI?Spring AI、LangChain4j、JBoltAI 实战对比
java·后端
__万波__16 小时前
二十三种设计模式(二十二)--策略模式
java·设计模式·策略模式
꧁Q༒ོγ꧂16 小时前
C++ 入门完全指南(六)--指针与动态内存
开发语言·c++
不想上班的小吕16 小时前
采购申请创建(BAPI_PR_CREATE/BAPI_REQUISITION_CREATE)
java·服务器·数据库
IT=>小脑虎16 小时前
2026版 Go语言零基础衔接进阶知识点【详解版】
开发语言·后端·golang
ChangYan.16 小时前
ffi-napi运行失败,报错:No native build was found,解决办法
开发语言
专注VB编程开发20年16 小时前
压栈顺序是反向(从右往左)的,但正因为是反向压栈,所以第一个参数反而离栈顶(ESP)最近。
java·开发语言·算法
椰汁菠萝16 小时前
spring boot下使用gdal解析tif文件
java·native·gdal·0
better_liang16 小时前
每日Java面试场景题知识点之-ELK日志分析
java·elk·微服务·面试题·日志分析·企业级开发