概述
- 概述
- 定位
- RocketMQ 是一个分布式消息中间件,用于在分布式系统中以异步方式传递时间/数据
- 典型问题
- 异步解耦
- 场景:下单后做扣库存、发优惠券、发短信、写审计日志
- 没用 MQ:下单接口串行调用 N 个下游 -> 延迟高、耦合重、下游挂了全挂
- 用 MQ:下单服务只负责写订单 + 发消息,下游异步消费,链路变短、隔离故障、便于扩展
- 削峰填谷
- 场景:大促瞬时 QPS 飙升,下游撑不住
- 用 MQ:把瞬时峰值写入 MQ,消费者按能力稳定拉取 -> 系统不被打爆
- 广播/集群消费
- 广播:配置刷新、缓存预热、全量通知
- 集群:订单履约、发货、对账(同组只要处理一次)
- 顺序/延迟/事务
- 顺序:同一订单状态流转必须按照顺序处理
- 延迟:30 分钟未支付自动关单
- 事务:下单写库 + 发消息要一致性
- 异步解耦
- 领域模型
- Topic/MessageQueue(并发与顺序的载体)
- Topic:逻辑分类
- MessageQueue(队列/分区):Topic 下的物理分片
- 并发的本质:多个队列=>多个消费者实例并行消费
- 顺序的边界:只保证同一队列内有序,跨队列无需
- 原因:MessageQueue 带来了水平扩展吞吐(分片并行)+提供队列内顺序语义
- Producer 生产者
- 生产者发送消息时,核心要决定:这条消息进入哪个队列
- 普通消息:轮询/负载均衡选择队列(提升吞吐)
- 顺序消息:按业务 key 做一致性路由到同一队列,保证顺序
- 生产者发送消息时,核心要决定:这条消息进入哪个队列
- ConsumerGroup 消费组
- 消费组:一组消费者实例共享同一套消费进度
- 集群消费:同组内分摊队列
- 广播消费:每个实例都处理全量消息
- 同一个 Topic,两个不同的 ConsumerGroup 相当于两套独立的订阅者,各自消费一份完整消息流
- Topic/MessageQueue(并发与顺序的载体)
- 卖点
- 高吞吐
- 顺序写:消息落到 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(推进进度)
- 订阅与分配
- 生产链路
- Topic
可靠性、语义、幂等、重试、死信
- 可靠性/语义/幂等/重试/死信
- 消息语义
- 三种语义
- At-most-once(至多一次):不重复,但可能丢
- At-least-once(至少一次):尽量不丢,但可能重复(RocketMQ 采用)
- Exactly-once(恰好一次):不丢不重
- 三种语义
- 为什么会重复
- 本质:分布式不确定性,无法在网络超时常见里判断对方是否已经成功
- 生产端导致重复:超时不等于失败
- Producer 发出了 send 请求
- Broker 实际写成功了,但 ack 在路上丢了/Producer 超时
- Producer 认为失败 -> 重试发送
- Broker 上存在两条业务等价消息
- 消费端导致重复:处理成功但提交 offset 失败/延迟
- Consumer 拉到消息并执行成功
- 在提交 offset 前发生进程重启/网络抖动/提交失败
- 下次启动从旧 offset 拉 -> 同一消息再次被处理
- 重试与死信:失败后到底怎么流转
- 消费失败 -> 重试:两类消费方式的区别
- 并发消费:消息可并行处理;失败后会触发稍后重投
- 顺序消费:同队列内严格顺序;失败会卡住队列,知道成功或打到策略
- 重试次数与延迟
- 消费失败后,消息会进入“重试主题/重试队列“
- 每次重试都会有一定的延迟
- 达到最大重试次数后仍然失败 -> 进入死信队列
- DLQ 常见命名 %DLQ%
- DLQ 工程治理
- 监控报警
- DLQ 堆积量、进入 DLQ 的速率、重试次数分布、消费失败率
- 分类原因
- 可重试:下游超时、偶发网络、锁冲突
- 不可重试:参数非法、业务校验失败、数据缺失
- 监控报警
- 消费失败 -> 重试:两类消费方式的区别
- Offset 管理
- Offset 是什么
- Offset 是 ConsumerGroup 在每个 MessageQueue 上的消费位置
- 不是全局一个值,而是 (group,topic,queueId) -> offset
- 正确提交时机
- 处理成功后再提交
- Offset 是什么
- 消息语义
性能与扩展、顺序与分区、堆积治理
- 性能与扩展、顺序与分区、堆积治理
- 吞吐与并发怎么调
- 吞吐链路拆解,多个环节的 min
- Producer 发送能力
- Broker 写入能力
- Consumer 拉取与处理能力
- 队列分片与分配:队列数决定
- 并发的硬上限:Queue 数决定可扩展性
- 集群消费模式下,同一个 ConsumerGroup 内,一个 MessageQueue 同一时刻只会分配给一个消费者实例
- 有效消费实例并行度 <= 队列数 Q
- 有 C 个实例:有效利用 = min(Q,C)
- 线程数只能提高单实例处理并行,队列数太少时,扩容实例不涨吞吐
- 调整的三类杠杆
- 队列数
- 现象:加消费者实例吞吐不涨/涨很少
- 原因:Q 太小,很多实例分不到队列
- 操作:提高 Topic 队列数
- 消费者实例数
- 前提:Q 足够
- 收益:线性提升
- 消费者线程数/批量参数
- 适用 Q 足够但实例受限或业务处理轻的场景
- 队列数
- 吞吐链路拆解,多个环节的 min
- 堆积定位
- 堆积的数学本质
- 堆积增长 = 生产速率 > 消费速率,差值积累在 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 做快速查询
- CommitLog:消息本体的唯一真相源
- 写入流程
- 流程
- 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 日志复制,从而提高一致性并提供自动选主
- 代价:写路径更重,延迟更高,但一致性更强、切换更自动化
- 传统 Master/Slave:复制延迟与一致性
- Broker 存储核心