从 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,从而实现消息投递与本地事务的最终一致性。

相关推荐
程序帝国2 小时前
配合上一个文章
spring boot
Miss_Chenzr3 小时前
Springboot快递信息管理52c05本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
java·数据库·spring boot
千寻技术帮3 小时前
基于SpringBoot的仿知乎知识问答系统
java·spring boot·毕业设计·论坛·文答
南昌彭于晏3 小时前
解决springboot静态内部类非空校验无效的问题
java·spring boot·后端
czlczl200209253 小时前
MybatisPlusInterceptor实现无感修改SQL的底层原理(源码)
数据库·spring boot·后端·sql
fanruitian3 小时前
springboot openai 调用functioncall
java·spring boot·spring·ai·springai