RocketMQ 复习二月 18, 2026概述# - 概述 - 定位 - RocketMQ 是一个分布式消息中间件,用于在分布式系统中以异步方式传递时间/数据 - 典型问题 - 异步解耦 - 场景:下单后做扣库存、发优惠券、发短信、写审计日志 - 没用 MQ:下单接口串行调用 N 个下游 -> 延迟高、耦合重、下游挂了全挂 - 用 MQ:下单服务只负责写订单 + 发消息,下游异步消费,链路变短、隔离故障、便于扩展 - 削峰填谷 - 场景:大促瞬时 QPS 飙升,下游撑不住 - 用 MQ:把瞬时峰值写入 MQ,消费者按能力稳定拉取 -> 系统不被打爆 - 广播/集群消费 - 广播:配置刷新、缓存预热、全量通知 - 集群:订单履约、发货、对账(同组只要处理一次) - 顺序/延迟/事务 - 顺序:同一订单状态流转必须按照顺序处理 - 延迟:30 分钟未支付自动关单 - 事务:下单写库 + 发消息要一致性 - 领域模型 - Topic/MessageQueue(并发与顺序的载体) - Topic:逻辑分类 - MessageQueue(队列/分区):Topic 下的物理分片 - 并发的本质:多个队列=>多个消费者实例并行消费 - 顺序的边界:只保证同一队列内有序,跨队列无需 - 原因:MessageQueue 带来了水平扩展吞吐(分片并行)+提供队列内顺序语义 - Producer 生产者 - 生产者发送消息时,核心要决定:这条消息进入哪个队列 - 普通消息:轮询/负载均衡选择队列(提升吞吐) - 顺序消息:按业务 key 做一致性路由到同一队列,保证顺序 - ConsumerGroup 消费组 - 消费组:一组消费者实例共享同一套消费进度 - 集群消费:同组内分摊队列 - 广播消费:每个实例都处理全量消息 - 同一个 Topic,两个不同的 ConsumerGroup 相当于两套独立的订阅者,各自消费一份完整消息流 - 卖点 - 高吞吐 - 顺序写:消息落到 CommitLog -> 磁盘友好 - 零拷贝/高效网络传输:减少内核态/用户态拷贝开销 - 批量与异步:批量写入、批量拉取、异步刷盘/复制 - 读写分离:写走 CommitLog,读通过索引定位 - 可扩展 - Topic 多队列 -> 消费端扩容实例可提升并发 - Broker 集群扩容 -> 分摊存储与 I/O 压力 - NameServer 做路由发现 -> Producer/Consumer 动态感知集群变化 - 靠队列分片 + Broker 横向扩展 + 消费组负载均衡 - 语义能力 - 至少一次投递与重复消费 - RocketMQ 工程实践通常是至少一次:可能重复,但不丢 - 业务必须幂等 - 重试与死信 - 消费失败 -> 重试 - 达到最大次数仍然失败 -> 进入死信队列 - 生产实践:DLQ 监控高进 + 人工/自动补偿 + 重新投递 - 事务消息(最终一致性) - 目标:本地事务与消息发送一致 - 核心机制:半消息(先存 broker 不投递)-> 本地事务 -> commit 才投递;不确定则回查 - 它解决的是最终一致性,不是强一致 ACID 核心概念# - 核心概念 - Topic - 定义:消息的逻辑分类,Producer 发送到 Topic,Consumer 订阅 Topic。 - 关键点:Topic 本身不决定并发,并发来自 Topic 下的 MessageQueue 数量 - 一个 Topic 通常会在多个 Broker 上分布存储,每个 Broker 上持有一些队列分片 - MessageQueue - 定义:Topic 的物理分片,是投递与顺序的基本单元。 - 并发能力=队列数上限:同哦个 ConsumerGroup 里,一个队列同一个时刻只会被一个消费者实例消费 - 顺序边界:RocketMQ 的顺序语义通常是队列内有序,跨队列不保证 - 路由载体:Producer 发送时必须选择一个队列(轮询/哈希/自定义) - Producer 生产者 - 职责:构造消息、选择 Topic 队列、发送、处理失败重试 - Producer 会先拿到 Topic 的路由信息(队列分布在哪些 Broker) - 发送失败通常会重试/换 Broker(取决于客户端策略与路由状态) - 发送模式 - 同步:拿到 sendResult 才返回(最常用) - 异步:回调(高吞吐/低延迟) - 单向:不关心结果(日志/打点) - Consumer(消费者) - 职责:拉取消息(Pull 体系,Push 是封装)、本地处理、成功后提交 offset,失败触发重试 - RocketMQ PushConsumer 本质是客户端内部循环 Pull + 回调你业务代码 - 消费并发:由消费线程数 + 队列分配决定 - ConsumerGroup(消费组) - 定义:一组消费者实例,共享同一套消费进度(offset),一起消费 Topic。 - CLUSTERING(集群消费):同组分摊消费(每条只处理一次) - BROADCASTING(广播消费):每个实例都处理全量(各自进度) - NameServer - 定位:路由注册与发现(轻量注册中心,不像 Kafka 把协调状态放在上面) - Broker 启动后像 NameServer 注册:Topic -> Broker -> 队列信息 - Producer/Consumer 定期从 NameServer 拉取最新路由 - NameServer 挂了是否可以发消息 - 短期通常能发(客户端还有路由缓存),但路由无法更新;长期不行,且新 Topic/新 Broker 不可见。 - Broker - 职责:接收消息,持久化存储,为消费者提供拉取、维护消费进度相关能力、做复制。 - 消息一般先写 CommitLog (顺序写),再构建队列索引。 - 吞吐高的原因:顺序写+批量+高效 IO 路径 - Offset (消费进度) - 定义:ConsumerGroup 在每个 MessageQueue 上的消费位置 - 成功消费 -> 提交 offset - 失败 -> 不提交 -> 后续重试/再次提交 - 数据流 - 生产链路 - 拿路由 - Producer 启动后从 NameServer 拉取 Topic 路由:有哪些 Broker、每个 Broker 上有哪些队列 - 选队列 - Produer 根据策略选择一个 MessageQueue - 普通消息:轮询/负载均衡 - 顺序消息:按业务 key 哈希到固定队列 - 发送到 Broker - Producer 把消息发送到队列所在 Broker - 持久化与应答 - Broker 持久化成功后返回 sendResult,Producer 认为发送成功 - 消费链路 - 订阅与分配 - Consumer 加入 ConumserGroup,订阅 Topic - 在集群模式下,同组实例会进行队列分配:每个 MessageQueue 分配给某个实例 - 拉取 - 被分配到队列的 Consumer 维护该队列的 offset,向 Broker 发起拉取请求 - 拉取这个队列从 offset=N 开始的下一批消息 - 本地处理 - Consumer 收到消息后调用你的业务处理逻辑(线程池并发执行) - 提交 Offset - 业务成功 -> 提交 offset(推进进度) 可靠性、语义、幂等、重试、死信# - 可靠性/语义/幂等/重试/死信 - 消息语义 - 三种语义 - At-most-once(至多一次):不重复,但可能丢 - At-least-once(至少一次):尽量不丢,但可能重复(RocketMQ 采用) - Exactly-once(恰好一次):不丢不重 - 为什么会重复 - 本质:分布式不确定性,无法在网络超时常见里判断对方是否已经成功 - 生产端导致重复:超时不等于失败 - Producer 发出了 send 请求 - Broker 实际写成功了,但 ack 在路上丢了/Producer 超时 - Producer 认为失败 -> 重试发送 - Broker 上存在两条业务等价消息 - 消费端导致重复:处理成功但提交 offset 失败/延迟 - Consumer 拉到消息并执行成功 - 在提交 offset 前发生进程重启/网络抖动/提交失败 - 下次启动从旧 offset 拉 -> 同一消息再次被处理 - 重试与死信:失败后到底怎么流转 - 消费失败 -> 重试:两类消费方式的区别 - 并发消费:消息可并行处理;失败后会触发稍后重投 - 顺序消费:同队列内严格顺序;失败会卡住队列,知道成功或打到策略 - 重试次数与延迟 - 消费失败后,消息会进入“重试主题/重试队列“ - 每次重试都会有一定的延迟 - 达到最大重试次数后仍然失败 -> 进入死信队列 - DLQ 常见命名 %DLQ%<ConsumerGroup> - DLQ 工程治理 - 监控报警 - DLQ 堆积量、进入 DLQ 的速率、重试次数分布、消费失败率 - 分类原因 - 可重试:下游超时、偶发网络、锁冲突 - 不可重试:参数非法、业务校验失败、数据缺失 - Offset 管理 - Offset 是什么 - Offset 是 ConsumerGroup 在每个 MessageQueue 上的消费位置 - 不是全局一个值,而是 (group,topic,queueId) -> offset - 正确提交时机 - 处理成功后再提交 性能与扩展、顺序与分区、堆积治理# - 性能与扩展、顺序与分区、堆积治理 - 吞吐与并发怎么调 - 吞吐链路拆解,多个环节的 min - Producer 发送能力 - Broker 写入能力 - Consumer 拉取与处理能力 - 队列分片与分配:队列数决定 - 并发的硬上限:Queue 数决定可扩展性 - 集群消费模式下,同一个 ConsumerGroup 内,一个 MessageQueue 同一时刻只会分配给一个消费者实例 - 有效消费实例并行度 <= 队列数 Q - 有 C 个实例:有效利用 = min(Q,C) - 线程数只能提高单实例处理并行,队列数太少时,扩容实例不涨吞吐 - 调整的三类杠杆 - 队列数 - 现象:加消费者实例吞吐不涨/涨很少 - 原因:Q 太小,很多实例分不到队列 - 操作:提高 Topic 队列数 - 消费者实例数 - 前提:Q 足够 - 收益:线性提升 - 消费者线程数/批量参数 - 适用 Q 足够但实例受限或业务处理轻的场景 - 堆积定位 - 堆积的数学本质 - 堆积增长 = 生产速率 > 消费速率,差值积累在 broker - 堆积原因分类 - 消费变慢 - 失败重试太多 - 生产突增 - Broker 压力抖动 - 堆积排查步骤 - 确定堆积是全局还是局部 - 如果是局部队列对接,大概率是热点 key/顺序路由/某实例异常 - 消费失败率与重试量 - 失败率高 -> 先止血,否则越修越崩 - 识别同一类异常栈是否集中 - 看单消息处理耗时分布 - 平均不重要,重点看 p95/p99 - p99 高往往来自:慢 SQL、远程调用尾延迟、锁竞争 - 看下游容量 - 看队列数与分配 - 看 Broker 层资源 - 堆积治理手段 - 止血 - 限流生产端:控制入口 - 失败隔离:把异常消息旁路到补偿 Topic/DLQ,避免拖垮主消费 - 降级业务逻辑:先做核心动作,其他异步/延后 - 恢复吞吐 - 扩容消费者实例 - 提高批量拉取/批量处理 - 优化热点:把热点 key 拆散 - 若某些队列极端堆积:考虑迁移/重新分配队列所在 broker - 根治 - 优化热点:慢 SQL、缓存、异步化、批处理 - 调整队列数与 key 策略 - 建立 DLQ/补偿闭环与告警 - 压测与容量规划:明确峰值可承受的 TPS 与延迟 存储与高可用# - 存储与高可用 - Broker 存储核心 - 三类文件 - CommitLog:消息本体的唯一真相源 - 所有 Topic 的消息都会追加吸入 CommitLog(不是每个队列一个日志,而是统一日志) - 写入模型是 append-only(追加写),是 RocketMQ 的高吞吐基础之一 - ConsumeQueue:面向消费的队列索引 - 消费者按照 MessageQueue 语义来消费,CommitLog 是全局混合写入的,需要一个队列视角的索引结构把队列映射回 CommitLog - ConsumeQueue 的本质是:每个 (topic, queueId) 一套逻辑队列索引,记录这条队列消息在 CommitLog 的物理位置等信息 - ConsumeQueue 是可重建的-Broker 异常后基于 CommitLog 恢复并重建缺失的 ConsumeQueue 索引,因此 ConsumeQueue 对绝对不丢要求没那么高 - IndexFile:按 key 查询的哈希索引 - 用于按 message key 做快速查询 - 写入流程 - 流程 - Producer 把消息发送到 Broker - Broker 顺序追加写 CommitLog - 再异步/批量地更新 ConsumeQueue/IndexFile - 原因 - 顺序写 CommitLog 最快 - 索引可以延迟/批量构建,并且 ConsumeQueue 丢了也能重建 - 读取链路 - 消费者按队列 (topic, queueId) 消费 - Consumer 拉取时携带 offset - Broker 通过 ConsumeQueue 把队列 offset 映射到 CommitLog 的物理位置 - 再从 CommitLog 把消息读出来返回给 Consumer - 吞吐高:顺序写+mmap+零拷贝+批量 - 顺序写:CommitLog append-only 模式写磁盘 - mmap 内存映射与 MappedFile 抽象 - Broker 用 mmap 把文件映射为虚拟内存,写入像写内存一样,减少传统 read/write 的系统调用与拷贝成本 - 零拷贝方向 - 批量 - 批量写入、批量拉取能显著摊薄网络往返与系统调用开销 - HA:复制、切换、延迟与一致性取舍 - 传统 Master/Slave:复制延迟与一致性 - Master 负责写入,Slave 复制数据做冗余 - 关键变量:复制是同步还是异步,直接决定 - RPO(数据丢失窗口):异步复制可能丢失最近一段 - RTO(恢复时间):切换/重连需要时间 - 自动主从切换:Controller - Controller:把谁是主、怎么选主、怎么切这个协调逻辑集中管理,从而支持自动故障转移 - 切换时关注 - 选主/仲裁机制 - 路由更新 - 复制进度 - 业务容忍度 - RocketMQ-on-DLedger(Raft):更强一致的 HA 思路 - DLedge 把 CommitLog 的复制变成 Raft 日志复制,从而提高一致性并提供自动选主 - 代价:写路径更重,延迟更高,但一致性更强、切换更自动化