跳转到内容

事务消息与回查

用途:使用 RocketMQ 类中间件做跨资源一致性时查阅

事务消息通过“半消息 + 二次确认 + 回查”把消息投递与本地事务绑定:

  1. 生产者发送半消息(对消费者不可见)到 Broker。
  2. Broker 存储成功后,生产者执行本地事务。
  3. 生产者提交或回滚:提交则半消息对消费者可见;回滚则删除。
  4. 若生产者长时间未确认(进程崩溃、网络断),Broker 回查生产者:由生产者检查本地事务的真实结果(查库、查状态表)并答复提交或回滚。

回查是这套机制的灵魂:它把“生产者没来得及表态”的悬置状态交给一个确定性的判定过程。

它解决了什么、没解决什么

「它解决了什么、没解决什么」小节链接
已解决 未解决
本地事务成功但消息丢失 消费方执行失败(需要消费重试 + 幂等)
本地事务失败但消息已发出 端到端恰好一次(仍是至少一次语义)
生产者崩溃后的悬置消息 业务语义冲突(需对账发现)

回查逻辑必须基于本地事实判定,不能依赖内存状态:

  • 查询业务表或事务状态表,判断本地事务是否已提交。
  • 明确“未知”处理策略:返回 UNKNOWN 让 Broker 稍后再查,而不是猜。
  • 设置回查上限与告警:超过阈值说明有真实故障,需要人工介入。
维度 事务消息 本地消息表 / Outbox
可靠性归属 中间件 自己的数据库
额外表与投递器 不需要 需要
可审计与重放 依赖中间件能力 表内可查、可重放
换中间件的迁移成本 语义依赖具体产品 与中间件解耦
回查代码 必须实现且必须正确 不需要

经验判断:如果消息中间件就是 RocketMQ 且团队接受其运维模型,事务消息更省事;如果需要跨多种通道(HTTP 回调、多个 MQ、外部系统)或要求事件可审计重放,Outbox 更通用。

误区 修正
回查里凭内存标志判断 必须查持久化状态
认为事务消息=恰好一次 消费端仍需幂等
本地事务里包含远程调用 事务持锁时间不可控,回查也可能超时
忽略回查次数上限告警 悬置消息静默堆积,问题在下游才暴露
半消息发送失败未处理 发送失败要能重试或走补偿路径
  • Apache RocketMQ 官方文档:事务消息
  • Chris Richardson《微服务架构设计模式》第 8 章(事务消息作为 Outbox 的替代形态)