RocketMQ 顺序消息、事务消息与延迟消息¶
30 秒版(开场)¶
顺序消息:同一消息组/Queue 内维持 FIFO;事务消息:半消息 → 本地事务 → commit/rollback/回查,缩小“写库成功但消息未发”的窗口,最终仍依赖本地事务状态、回查和消费幂等;延迟消息要区分 4.x 固定级别与 5.x 定时时间戳及其范围限制。
3 分钟版(精讲深度)¶
- 顺序:全局顺序单 Queue(吞吐低);分区顺序用 ShardingKey hash 到固定 Queue;消费端 MessageListenerOrderly 单线程 per Queue。
- 事务:
sendMessageInTransaction→ Broker 存半消息(对消费者不可见)→ 执行本地事务 →commit后可见或rollback后丢弃;若 broker 没拿到确定结果会回查本地事务状态。 - 延迟:RocketMQ 4.x:
setDelayTimeLevel(n),18 档(1s…2h),非任意 timestamp。5.0+:支持DELIVERY_TIMESTAMP任意时刻;老系统仍常见 18 档。
10 分钟版(原理 + 图示)¶
事务消息流程
sequenceDiagram
participant P as Producer
participant B as Broker
participant DB as Local DB
participant C as Consumer
P->>B: 半消息 PREPARED
P->>DB: 本地事务
alt success
P->>B: COMMIT
B->>C: 投递
else fail
P->>B: ROLLBACK
end
B-->>P: 事务回查 checkLocalTransaction
| 类型 | 关键点 | 坑 |
|---|---|---|
| 顺序 | ShardingKey 同 key 同 Queue | 消费失败阻塞该 Queue |
| 事务 | 回查接口必须可靠 | 回查风暴、状态不明 |
| 延迟 4.x | 18 固定级别 | 非任意 timestamp |
| 延迟 5.x | 按定时时间戳投递 | 有最大时间范围等 broker 限制,API 与 4.x 不同 |
flowchart LR
P[Producer] -->|delay level / timer| SCH[Schedule]
SCH -->|到期| Q[MessageQueue]
Q --> C[Consumer]
生产场景¶
- 订单状态机:同一 orderId 顺序消费状态变更
- 支付成功发积分:事务消息保证「账已记才发 MQ」
- 30 分钟未支付关单:延迟 level 或延迟 Topic + 定时扫描
排查与工具¶
- 事务消息堆积在
RMQ_SYS_TRANS_HALF_TOPIC - 顺序消费卡住:看该 Queue offset 是否停在某条 poison message
- DLQ:
%DLQ%{consumerGroup}
架构取舍¶
| 方案 | 适用 |
|---|---|
| RocketMQ 事务消息 | 已有 RocketMQ、接受回查 |
| 本地消息表 / Outbox | 任意 MQ、DB 同事务写 outbox |
| Saga/TCC | 跨多服务长事务 |
深挖问答¶
- 顺序消息消费失败? → 重试阻塞同 Queue;需 skip 策略或 DLQ + 人工。
- 事务回查做什么? → 查本地事务表/订单状态,返回 COMMIT/ROLLBACK/UNKNOWN。
- 延迟不准? → 4.x level 粒度粗;5.x 到期时间也不等于业务代码精确在该毫秒执行,仍受 broker/consumer 排队影响。海量同一时刻任务还会形成峰值。
- 和 Kafka 事务? → Kafka 事务是 broker 层原子写多分区;RocketMQ 是业务本地事务 + 半消息。
反模式与事故¶
- 所有消息都走顺序 → 吞吐暴跌
- 回查逻辑返回 UNKNOWN 过久 → 半消息堆积
- 延迟 level 当精确调度 → 业务时间误差
代码示例¶
// 顺序发送:同一 orderID 作为 ShardingKey
msg.WithShardingKey(orderID)
// 延迟:level 3 通常为 10s(以 broker 配置为准)
msg.WithDelayTimeLevel(3)