从 ACK 到事务裁决:Spring Boot 中 RocketMQ 事务消息的完整工作机制解析

一、背景:为什么要引入事务消息

在高并发下单、支付、状态变更等核心业务中,我们通常会采用如下流程:

1. 本地事务(订单落库)

2. 发送 MQ 消息(通知下游系统)

但这个流程存在一个致命问题

数据库事务与消息发送并不具备原子性

一旦出现:

  • 数据已提交

  • 消息发送失败

系统就会进入数据不一致状态

为了解决这个问题,RocketMQ 提供了 事务消息机制,用于保证:

本地事务执行结果 与 消息投递结果的最终一致性


二、事务消息的核心概念:Half Message(半消息)

RocketMQ 事务消息并不是"先执行事务,再发消息",而是采用了一个中间态设计:

Half Message(也称 Prepare Message)

半消息的特点:

  • 已经被 Broker 持久化

  • 对消费者 不可见

  • 等待 Producer 给出"事务裁决"

这是整个事务消息机制的基础。


三、从代码入口看完整流程

在 Spring Boot 中,事务消息的入口通常是:

java 复制代码
rocketMQTemplate.sendMessageInTransaction( "order-created-topic", message, businessArg );

这一行代码背后,隐藏的是一整套 严格定义的时序流程


四、事务消息的完整时序拆解(关键)

阶段一:Producer 发送半消息

Producer 首先向 Broker 发送一条 Prepare Message

  • Broker 接收到后:

    • 将消息持久化

    • 标记为 PREPARED

    • 不会投递给任何消费者

此时 Broker 会返回一个 ACK。

注意:
这个 ACK 仅代表"半消息存储成功",不代表事务成功


阶段二:ACK 返回后,事务监听器被触发

这是很多开发者最容易误解的地方。

很多人以为:

收到 ACK → 再回调 → 再触发事务监听器

但真实情况是:

RocketMQ 客户端在收到半消息 ACK 后,会立即在同一调用链路中触发本地事务执行

也就是说:

  • ACK 不会"传递"给你

  • 你也无需监听 ACK

  • 一切由 RocketMQ 客户端内部状态机控制


五、事务监听器的真实角色

在 Spring Boot 中,事务监听器通常实现:

java 复制代码
public class OrderTransactionListener
        implements RocketMQLocalTransactionListener

executeLocalTransaction:事务裁决入口

java 复制代码
@Override
public RocketMQLocalTransactionState executeLocalTransaction(
        Message msg, Object arg) {
    // 执行本地事务
}

这个方法的调用时机是:

半消息发送成功(ACK 返回)之后,立即调用

此时你要做的事情只有一件:

执行本地事务,并给出事务裁决结果


事务监听器返回值的真正含义

java 复制代码
return RocketMQLocalTransactionState.COMMIT;

这行代码的本质含义是:

Producer 明确告诉 Broker:
本地事务已经成功,可以提交这条消息

RocketMQ 提供了三种裁决结果:

返回值 含义
COMMIT 本地事务成功,消息可投递
ROLLBACK 本地事务失败,消息丢弃
UNKNOWN 状态不确定,等待回查

这里不是"接收 ACK",而是"向 Broker 给最终裁决"


六、Broker 如何感知事务结果

COMMIT

  • Broker 将半消息转为普通消息

  • 消费者开始可见

ROLLBACK

  • Broker 删除半消息

  • 消息彻底结束生命周期

UNKNOWN

  • Broker 将该消息标记为"待确认"

  • 稍后主动发起 事务回查


七、事务回查机制(第二道保险)

当出现以下情况之一时:

  • Producer 返回 UNKNOWN

  • Producer 在执行本地事务时宕机

  • Broker 长时间未收到事务裁决

Broker 会主动回查 Producer。


回查入口方法

java 复制代码
@Override
public RocketMQLocalTransactionState checkLocalTransaction(
        Message msg)

回查的本质

Broker 并不是相信 Producer 的返回值

而是要求 Producer 基于真实数据状态重新裁决

典型做法是:

  • 查询数据库中订单是否存在

  • 根据结果返回 COMMIT 或 ROLLBACK


八、事务监听器如何与 Producer 绑定

主要是通过RocketMQTemplate进行绑定的,所以在多template场景下才需要进行绑定

java 复制代码
​
@RocketMQTransactionListener(
    rocketMQTemplateBeanName = "orderRocketMQTemplate"
)

​

九、一个容易踩坑但非常重要的设计原则

在事务监听器中:

不建议直接注入 Repository

强烈建议通过 Service 层执行业务逻辑

原因在于:

  • 事务监听器由 RocketMQ 客户端线程触发

  • 本地事务、代理、AOP 需要 Spring 正确管理

  • Service 层是事务边界的最佳落点


十、用一句话总结 RocketMQ 事务消息的本质

RocketMQ 事务消息并非通过 ACK 回调驱动业务逻辑,而是通过"半消息 + 本地事务裁决 + 事务回查"机制,将消息提交的最终决定权交由 Producer,从而实现消息投递与本地事务的最终一致性。

相关推荐
程序员晓琪16 小时前
约定大于配置:基于 Java 包名自动生成 API 版本路由的最佳实践
java·spring boot·后端
Flittly17 小时前
【AgentScope Java新手村系列】(11)中断与恢复
java·spring boot·spring
用户3521802454752 天前
🎆从 Prompt 到 Skill:让 Spring AI Agent 学会"装新技能"
人工智能·spring boot·ai编程
用户3521802454755 天前
当 Prompt 学会"热更新":Spring Boot × Nacos3 AI 实战
java·spring boot·ai编程
昵称为空C5 天前
手撸一个动态 SQL 执行引擎:不重启服务,在线增删改查任意数据库
spring boot·后端
霸道流氓气质6 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
于先生吖6 天前
SpringBoot对接大模型开发AI命理测算系统:八字排盘与AI解析接口源码全解
人工智能·spring boot·后端
Flittly6 天前
【AgentScope Java新手村系列】(10)实战-多Agent天气助手
java·spring boot·spring
星落zx6 天前
Spring Boot 多模型集成:优雅调用全球主流大模型
人工智能·spring boot·chatgpt