RocketMQ 都有哪些消息类型?使用场景是什么?
系统介绍 RocketMQ 5.x 的普通消息、顺序消息、定时与延时消息、事务消息,说明各自语义、适用场景、使用限制,并澄清批量消息、重试消息和死信消息是否属于消息类型。
RocketMQ 都有哪些消息类型?使用场景是什么?
面试回答
RocketMQ 5.x 按消息的传输语义正式分为四种类型:
- 普通消息 Normal:没有顺序、定时或事务等特殊语义,发送成功后即可被消费,适合微服务解耦、异步通知、日志和数据集成。
- 顺序消息 FIFO:同一 Message Group 内的消息按照发送、存储和投递顺序处理,适合同一订单的创建、支付、发货等状态流转,以及 Binlog 增量同步。
- 定时与延时消息 Delay:消息到指定时间后才对消费者可见,适合订单超时关闭、延迟检查、定时通知和任务调度。
- 事务消息 Transaction:通过半事务消息、本地事务确认和事务回查,保证本地事务与消息发送的最终一致性,适合下单后发放积分、扣款后发送通知等场景。
RocketMQ 5.x 可以为 Topic 声明消息类型,一个 Topic 只应承载一种消息类型。批量消息属于发送方式,重试消息和死信消息属于消费失败后的系统处理状态,Request-Reply 属于通信模式,它们都不属于上述四种正式 MessageType。
一句话总结:
无特殊要求用普通消息;同一业务实体需要按序处理用 FIFO;需要未来触发用 Delay;本地事务必须和消息发送保持最终一致用 Transaction。
一张图看懂
详细讲解
一、先明确 RocketMQ 的正式分类
RocketMQ 5.x 定义了四种 MessageType:
| 消息类型 | 核心语义 | 典型场景 |
|---|---|---|
NORMAL | 发送后立即进入可消费状态 | 异步解耦、事件通知、日志传输 |
FIFO | 同一 Message Group 内按顺序处理 | 订单状态流转、Binlog 同步 |
DELAY | 到指定时间后才允许消费 | 超时关闭、延迟检查、定时任务 |
TRANSACTION | 本地事务与消息发送最终一致 | 交易完成后驱动下游业务 |
这四种类型不是按照消息内容划分,而是按照消息需要具备的传输和投递语义划分。
例如,同样是“订单创建”事件:
- 创建后立即异步通知风控,可以使用普通消息;
- 必须与后续支付、发货事件保持同一订单内的顺序,可以使用顺序消息;
- 创建三十分钟后检查是否支付,可以使用延时消息;
- 只有订单数据库事务成功后才能向下游投递,可以使用事务消息。
因此,消息类型的选择取决于业务语义,而不是消息中装了什么数据。
二、普通消息 Normal
1. 什么是普通消息
普通消息没有顺序、定时和事务等特殊语义。Broker 接收并保存消息后,消息进入 Ready 状态,Consumer 可以正常获取并处理。
Producer
│ 发送
▼
Broker 持久化
│ 立即可见
▼
Consumer 消费
“普通”不代表不可靠。普通消息同样可以使用发送确认、Broker 持久化、消费重试、死信队列和业务幂等机制。
它只表示:
消息之间没有需要 RocketMQ 额外维护的顺序、时间或事务关系。
2. 适用场景
普通消息适合绝大多数异步业务:
- 微服务之间异步解耦;
- 用户注册后异步发送欢迎短信;
- 订单创建后异步更新搜索索引;
- 操作日志、埋点和审计事件传输;
- 缓存失效通知;
- 数据采集和数据集成;
- 不要求严格顺序的业务事件广播。
例如,订单创建后需要通知多个独立系统:
订单服务
│
└── OrderCreated 普通消息
├── 风控服务
├── 推荐服务
└── 数据分析服务
这些下游之间不存在必须先后执行的关系,使用普通消息能够获得更好的并发能力。
3. 不适合的场景
以下要求不能只依赖普通消息:
- 同一订单的支付必须在创建之后处理;
- 消息必须在未来某个时间触发;
- 本地数据库事务失败时消息绝对不能对下游可见。
这时应分别考虑 FIFO、Delay 或 Transaction。
三、顺序消息 FIFO
1. 顺序消息保证什么
RocketMQ 5.x 使用 Message Group 标识需要保持顺序的一组消息。同一个 Message Group 中的消息按照先进先出顺序处理。
例如以订单号作为 Message Group:
Message Group = order-1001
创建订单 → 支付成功 → 仓库出库 → 物流发货
另一个订单可以使用不同的 Message Group:
order-1001:创建 → 支付 → 发货
order-1002:创建 → 取消
两个订单之间不要求顺序,可以并发处理;同一订单内部保持顺序。这通常称为局部顺序或分组顺序。
2. 为什么不追求整个 Topic 的全局顺序
如果一个 Topic 的全部消息都要求全局顺序,就只能把所有消息串行发送、存储和消费,并发能力会受到严重限制。
按订单号、用户 ID、账户 ID 等业务键划分 Message Group,可以做到:
组内有序
+
组间并行
这更符合大多数业务的真实要求。
3. 保证顺序需要满足哪些条件
完整的顺序需要覆盖三个阶段。
发送顺序
同一 Message Group 的消息需要由生产端按业务顺序串行发送。如果多个线程或多个生产者同时发送,即使 Message Group 相同,RocketMQ 也无法推断业务上谁应该在前。
存储顺序
RocketMQ 将同一 Message Group 的消息存入同一个队列,并保持接收顺序。
消费顺序
Consumer 必须遵循“接收、处理、确认”的顺序。不能收到消息后随意提交给异步线程并立即确认,否则业务实际完成顺序仍可能被打乱。
发送有序
↓
存储有序
↓
投递有序
↓
业务处理有序
4. 适用场景
- 同一订单的创建、支付、取消和发货;
- 同一账户的充值、扣款和退款;
- 同一用户的状态变更;
- 数据库 Binlog 增量同步;
- 工作流节点推进;
- 交易撮合等要求按到达顺序处理的场景。
5. 使用代价与风险
顺序消息会牺牲部分并发能力,并且前面的消息处理失败时,后续消息可能等待。
需要重点关注:
- Message Group 是否过热;
- 单条消息是否持续重试并阻塞后续消息;
- 消费逻辑是否包含慢 SQL、慢 RPC;
- 是否错误地在消费端进行异步并发处理;
- 是否真的需要顺序,还是使用状态机和幂等就能解决。
不要把所有消息都设置为同一个 Message Group,否则会把系统退化成串行消费。
四、定时与延时消息 Delay
1. 定时消息和延时消息有什么区别
业务表达方式不同,但在 RocketMQ 5.x 中本质相同:都是设置一个未来的投递时间戳。
延时消息:10 分钟后投递
定时消息:2026-07-26 18:00:00 投递
延时消息也需要先计算出最终投递时间:
long deliveryTimestamp =
System.currentTimeMillis() + Duration.ofMinutes(10).toMillis();
2. 消息生命周期
Producer 发送
↓
Timing:Broker 按时间暂存
↓ 到达投递时间
Ready:转入普通存储并对 Consumer 可见
↓
Consumer 消费并确认
消息到达 Broker 并不代表 Consumer 立即可见。只有达到投递时间,消息才进入可以消费的状态。
3. 适用场景
- 订单创建三十分钟后检查是否支付;
- 支付处理中,五分钟后查询渠道结果;
- 优惠券到期提醒;
- 用户预约通知;
- 延迟重试;
- 定时清理任务;
- 分布式定时调度。
例如订单超时关闭:
创建订单
│
├── 写入订单数据
└── 发送 30 分钟后投递的 Delay 消息
↓
Consumer 查询订单状态
├── 未支付:关闭订单
└── 已支付:忽略消息
Consumer 收到延时消息后仍然必须重新检查业务状态。消息表达的是“现在应该检查”,不是“订单一定未支付”。
4. 延时消息不是精确定时器
设置的时间表示消息不应早于该时间被消费,但以下因素可能使实际处理晚于目标时间:
- Broker 当前负载较高;
- 大量消息设置了相同投递时间;
- 消费端发生积压;
- Consumer 执行较慢或暂时不可用;
- 存储系统故障或重启。
因此,对秒级严格准点有强要求的业务不能只依赖消息到达时间,还要监控投递延迟并准备补偿扫描。
5. RocketMQ 4.x 与 5.x 的区别
RocketMQ 4.x 常见实现使用固定的延迟级别,例如:
1 秒、5 秒、10 秒、30 秒、1 分钟……
生产者通过 delayTimeLevel 选择预设级别。
RocketMQ 5.x 的正式 Delay 消息使用毫秒级 Unix 时间戳表示投递时间,不再局限于旧版固定延迟级别。面试时需要先说明讨论的是哪个版本,避免把 4.x 的十八个延迟级别直接当作 5.x 的唯一实现。
五、事务消息 Transaction
1. 事务消息解决什么问题
事务消息解决的是:
本地事务与消息发送之间的最终一致性。
例如订单服务需要同时完成:
- 在本地数据库创建订单;
- 向 RocketMQ 发送订单创建消息。
如果先提交数据库再发送消息,数据库成功后消息可能发送失败;如果先发送消息再写数据库,消息可能已被消费,但数据库事务最终失败。
数据库事务成功,消息失败 → 下游永远不知道订单已创建
消息发送成功,数据库失败 → 下游处理了一笔不存在的订单
2. 核心流程
Producer 发送 Half Message
↓
Broker 保存消息,但暂不向 Consumer 投递
↓
Producer 执行本地事务
┌───┴────────┐
│ │
Commit Rollback
│ │
消息可见并投递 消息回滚
如果 Broker 长时间没有收到明确结果,会回查 Producer:
Broker 发起事务回查
↓
Producer 查询本地事务记录
┌────┼─────┐
Commit Unknown Rollback
Producer 的事务回查不能只依赖内存变量,需要根据订单表、事务日志或本地 Outbox 等持久化数据判断。
3. 适用场景
- 订单创建成功后通知库存、积分系统;
- 扣款成功后发送账务事件;
- 账户入账成功后通知下游;
- 数据库状态变化后更新搜索索引;
- 本地事务成功后驱动跨服务异步流程。
4. 事务消息不保证什么
事务消息保证的是本地事务与消息是否投递之间的最终一致性,不等于整个分布式业务已经成功。
例如:
订单本地事务成功
↓
事务消息 Commit
↓
积分服务消费失败
RocketMQ 可以重试投递,但积分服务仍需实现:
- 消费幂等;
- 失败重试;
- 死信处理;
- 对账与补偿;
- 业务状态机。
事务消息也不是跨多个数据库的强一致分布式事务。它更适合能够接受异步执行和最终一致性的业务。
六、四种消息类型如何选择
可以按照下面的顺序判断:
本地事务是否必须与消息发送保持最终一致?
├── 是 → Transaction
└── 否
↓
消息是否需要到未来某个时间才可消费?
├── 是 → Delay
└── 否
↓
同一业务实体的事件是否必须按顺序处理?
├── 是 → FIFO
└── 否 → Normal
对应关系:
| 业务要求 | 推荐类型 | 关键设计点 |
|---|---|---|
| 只需要可靠异步传输 | Normal | 重试、幂等、监控 |
| 同一订单内有先后关系 | FIFO | Message Group、串行处理 |
| 未来某个时间触发 | Delay | 投递时间、状态复查、延迟监控 |
| 本地事务成功后才能投递 | Transaction | Half Message、事务回查、持久化事务状态 |
不要因为高级消息类型“功能更多”就默认使用。每种特殊语义都会增加约束、运维成本或吞吐损耗;没有特殊要求时,普通消息通常是更简单的选择。
七、这些算不算消息类型
1. 批量消息
批量消息不是独立 MessageType,而是一种发送或消费方式。
单条发送:一次请求发送一条消息
批量发送:一次请求携带多条消息
批量能够减少网络往返和请求开销,适合日志采集、数据同步等吞吐优先的场景,但需要注意:
- 控制整个批次的大小;
- 批次失败后的重试粒度;
- 顺序消息不应随意并行批处理;
- 批量消费部分成功时的幂等和重试问题。
2. 重试消息
重试消息是普通消息、FIFO 消息等消费失败后,RocketMQ 根据消费重试策略重新投递形成的系统处理过程,不是业务创建 Topic 时选择的正式 MessageType。
第一次消费失败后,消息可能经历:
Ready → Inflight → WaitingRetry → Ready
Consumer 必须按消息可能重复投递来设计幂等。
3. 死信消息
消息超过最大消费重试次数后,可能进入死信队列。死信消息代表消息进入异常隔离状态,也不是四种正式业务 MessageType 之一。
死信队列需要配套:
- 告警;
- 失败原因记录;
- 人工检查;
- 修复后的重投;
- 对账和业务补偿。
4. Request-Reply 消息
Request-Reply 是一种通信模式:
请求方发送消息
↓
处理方消费并返回响应
↓
请求方等待响应
它描述生产者和消费者如何交互,不属于 NORMAL、FIFO、DELAY、TRANSACTION 之外的第五种正式 MessageType。
5. 同步、异步和单向消息
同步发送、异步发送和单向发送描述的是 Producer 调用发送接口的方式,也不是消息类型。
同步发送:等待 Broker 返回结果
异步发送:通过回调接收结果
单向发送:发送后不等待结果
不要把“发送方式”和“消息传输语义”混为一谈。
八、RocketMQ 5.x 为什么要求 Topic 类型一致
RocketMQ 5.x 支持在 Topic 上声明消息类型:
mqadmin updateTopic \
-n <nameserver_address> \
-t <topic_name> \
-c <cluster_name> \
-a +message.type=<NORMAL|FIFO|DELAY|TRANSACTION>
启用类型校验后,一个 Topic 只允许发送与其类型一致的消息。例如:
FIFO消息不能发送到NORMALTopic;TRANSACTION消息不能发送到DELAYTopic;- 同一个 Topic 不应同时承载普通消息和顺序消息。
这样做的价值是:
- 让 Topic 的行为语义清晰;
- 避免客户端误用高级消息能力;
- 便于 Broker 按消息类型管理;
- 降低运维和故障排查复杂度。
为了兼容旧客户端,5.x 的强制类型校验能力需要结合服务端配置判断。面试中可以说“5.x 支持并推荐一个 Topic 对应一种消息类型”,不要绝对化为所有兼容部署都默认强制拒绝。
九、常见追问
1. 普通消息一定无序吗
普通消息在某个物理队列中自然存在存储先后,但业务没有建立端到端 FIFO 约束。多队列发送、并发消费、失败重试都可能使业务完成顺序变化,因此不能依赖普通消息保证业务顺序。
2. 顺序消息能否保证整个 Topic 全局有序
实际通常保证同一 Message Group 内有序。理论上把所有消息放入一个组可以接近全局串行,但会形成热点并严重限制吞吐,不建议这样设计。
3. 延时消息到点后一定立即执行吗
不一定。到点表示消息可以进入可消费状态,Broker 调度延迟、消息积压和 Consumer 处理能力都会影响最终业务执行时间。
4. 事务消息消费成功是否与本地事务强一致
不是。事务消息只保证本地事务结果与消息是否投递之间的最终一致性。Consumer 的业务处理仍然是独立的异步过程,需要重试、幂等和补偿。
5. FIFO 和 Transaction 能否同时使用
从 RocketMQ 5.x 的 Topic 类型模型看,一个 Topic 只对应一种正式消息类型,不能把同一条消息同时声明为 FIFO 和 Transaction。业务同时需要“事务一致性”和“顺序处理”时,需要重新审视领域流程,常见做法是用事务消息可靠发布业务事件,再由下游状态机、版本号、业务序号或独立 FIFO 流程处理顺序要求。
十、容易答错的地方
错误一:RocketMQ 有五种消息,包含批量消息
批量是发送方式,不是 RocketMQ 5.x 的正式 MessageType。正式类型是 Normal、FIFO、Delay、Transaction。
错误二:顺序消息保证整个 Topic 全局有序
通常保证同一 Message Group 内的局部顺序,不同组之间允许并发。
错误三:延时消息到了时间就保证业务已经执行
到达投递时间只表示消息对 Consumer 可见,实际业务完成还受积压、消费能力和重试影响。
错误四:事务消息保证上下游所有业务一起提交
事务消息保证本地事务与消息投递最终一致,下游消费结果仍然需要独立保障。
错误五:普通消息可靠性低
普通消息只是没有高级传输语义,不代表不持久化、不重试或不可靠。
评论与讨论
回复 :
留下你的想法