事务消息与回查
用途:使用 RocketMQ 类中间件做跨资源一致性时查阅
定义
「定义」小节链接事务消息通过“半消息 + 二次确认 + 回查”把消息投递与本地事务绑定:
- 生产者发送半消息(对消费者不可见)到 Broker。
- Broker 存储成功后,生产者执行本地事务。
- 生产者提交或回滚:提交则半消息对消费者可见;回滚则删除。
- 若生产者长时间未确认(进程崩溃、网络断),Broker 回查生产者:由生产者检查本地事务的真实结果(查库、查状态表)并答复提交或回滚。
回查是这套机制的灵魂:它把“生产者没来得及表态”的悬置状态交给一个确定性的判定过程。
它解决了什么、没解决什么
「它解决了什么、没解决什么」小节链接| 已解决 | 未解决 |
|---|---|
| 本地事务成功但消息丢失 | 消费方执行失败(需要消费重试 + 幂等) |
| 本地事务失败但消息已发出 | 端到端恰好一次(仍是至少一次语义) |
| 生产者崩溃后的悬置消息 | 业务语义冲突(需对账发现) |
回查实现要求
「回查实现要求」小节链接回查逻辑必须基于本地事实判定,不能依赖内存状态:
- 查询业务表或事务状态表,判断本地事务是否已提交。
- 明确“未知”处理策略:返回 UNKNOWN 让 Broker 稍后再查,而不是猜。
- 设置回查上限与告警:超过阈值说明有真实故障,需要人工介入。
与 Outbox 的取舍
「与 Outbox 的取舍」小节链接| 维度 | 事务消息 | 本地消息表 / Outbox |
|---|---|---|
| 可靠性归属 | 中间件 | 自己的数据库 |
| 额外表与投递器 | 不需要 | 需要 |
| 可审计与重放 | 依赖中间件能力 | 表内可查、可重放 |
| 换中间件的迁移成本 | 语义依赖具体产品 | 与中间件解耦 |
| 回查代码 | 必须实现且必须正确 | 不需要 |
经验判断:如果消息中间件就是 RocketMQ 且团队接受其运维模型,事务消息更省事;如果需要跨多种通道(HTTP 回调、多个 MQ、外部系统)或要求事件可审计重放,Outbox 更通用。
常见误区
「常见误区」小节链接| 误区 | 修正 |
|---|---|
| 回查里凭内存标志判断 | 必须查持久化状态 |
| 认为事务消息=恰好一次 | 消费端仍需幂等 |
| 本地事务里包含远程调用 | 事务持锁时间不可控,回查也可能超时 |
| 忽略回查次数上限告警 | 悬置消息静默堆积,问题在下游才暴露 |
| 半消息发送失败未处理 | 发送失败要能重试或走补偿路径 |
延伸阅读
「延伸阅读」小节链接- Apache RocketMQ 官方文档:事务消息
- Chris Richardson《微服务架构设计模式》第 8 章(事务消息作为 Outbox 的替代形态)