Camel in Action事务与幂等性完全教程:XA分布式事务、补偿机制防重复消息实战
【免费下载链接】camelinaction2:camel: This project hosts the source code for the examples of the Camel in Action 2nd ed book :closed_book: written by Claus Ibsen and Jonathan Anstey.项目地址: https://gitcode.com/gh_mirrors/ca/camelinaction2
本项目是《Camel in Action》第二版的官方示例源码,由 Camel 创始人 Claus Ibsen 与 Jonathan Anstey 编写。本文带你快速掌握Apache Camel 事务的完整体系:从丢失消息的经典故事,到XA 分布式事务(两阶段提交)实战、补偿机制,再到用幂等性消费者根治重复消息问题。
为什么消息系统需要事务?💥
很多新手的第一反应是:消息队列已经很可靠了,还需要事务吗?
chapter12/riderautoparts-partner模块给出了教科书式的反面案例——"丢失消息的故事":某汽车零件公司用 JMS 队列接收合作伙伴的请求,消费端在"先确认消息、再写数据库"的流程中崩溃,消息永远丢失了。罪魁祸首是autoAcknowledge确认模式:消息还没处理完就被确认掉了。
正确做法是客户端确认(Client Acknowledge):处理成功后再手动确认。相关示例见 ClientAckBean.java,可用以下命令验证:
cd chapter12/riderautoparts-partner mvn test -Dtest=RiderAutoPartsPartnerClientAcknowledgeModeTest本地事务 vs XA 全局事务:一张表看懂
📌 这是选型时最关键的一步:
| 对比项 | 本地事务(Local) | XA 全局事务(2PC) |
|---|---|---|
| 适用场景 | 单一资源(一个队列或一个数据库) | 跨资源(消息队列 + 数据库) |
| 一致性保证 | 资源内部 | 所有资源原子提交/回滚 |
| 性能 | 高 | 较低(协调开销) |
| 对应示例 | tx-database/ | xa/ |
XA 即两阶段提交(2-Phase Commit):先让所有参与者"准备",全部就绪后统一"提交",任何一环失败则整体回滚。
XA 分布式事务实战:提交与回滚的完整流程
chapter12/xa是本章的核心实战模块,演示消息队列与数据库如何在一个 XA 事务中保持一致:
- ✅提交场景(
XACommitTest):从 ActiveMQ 消费消息并写入数据库,事务成功提交 - ✅回滚场景(
XARollbackBeforeDbTest/XARollbackAfterDbTest):分别在写库前、后抛出异常,验证数据库回滚、消息进入DLQ(死信队列)而不会丢失
以 SpringXARollbackAfterDbTest.java 为例,它模拟"消息已入队、数据库已写入、随后处理失败"的极端情况——最终数据库中 0 行记录、消息安全落在ActiveMQ.DLQ,这正是生产环境最需要的保证。
运行方式:
cd chapter12/xa mvn test -Dtest=SpringXACommitTest事务传播机制:REQUIRES_NEW 实现"部分回滚"
现实中最棘手的问题:一个步骤失败,难道前面成功的全都要回滚?
chapter12/propagation演示了事务传播策略(Propagation)。比如订单主流程与审计日志分别使用不同传播级别:订单回滚时,审计日志通过REQUIRES_NEW独立提交,保留故障现场。核心 Bean AuditLogService.java 会把每条订单连同JMSRedelivered(是否重投)标志写入审计表,是排障的黄金数据。
UnitOfWork 工作单元:没有事务时的补偿机制 🛠️
并非所有组件都支持事务。Camel 的UnitOfWork(工作单元)是它的"补偿"方案,chapter12/uow模块演示了三种关键能力:
- 同步回调(Synchronization):监听事务结束事件
- onCompletion 模式:只在全部路由执行成功后才确认消息——失败则不确认,消息会被重投而不是丢失
- BeforeConsumer 模式:消费前就标记,防止重复处理
该模块还附有一个完整的 REST 订单服务(OrderRoute.java),可用curl -i http://localhost:8080/service/order/123体验返回头中生成的防重复 token。
幂等性消费者:防重复消息的终极手段 🎯
消息重投、负载均衡重试、网络抖动……重复消息是分布式系统的家常便饭。
chapter12/idempotent用Idempotent Consumer(幂等性消费者)EIP优雅解决它。看 IdempotentTest.java 的路由设计:
from("seda:inbox") .idempotentConsumer(header("orderId"), repo) .to("mock:order") .end();路由中用idempotentConsumer以orderId消息头作为唯一键,配合仓库对象repo记录已处理的消息。测试一次性发送 5 条订单消息(其中 2 条重复),最终只有3 条唯一订单被处理——重复消息被自动过滤,且断言保证了零重复。
开箱即用的幂等仓库实现
Camel 自带多种 IdempotentRepository 实现,按需选择:
- MemoryIdempotentRepository:内存实现,示例默认使用,重启即失效
- InfinispanIdempotentRepository:基于 Infinispan 缓存,适合集群共享
- JCache / JPA:标准化缓存或持久化到数据库,重启后依然生效
下图展示了 Infinispan 控制台中的缓存视图,幂等键数据即存储于此:
快速上手:三步跑通全部示例
1️⃣ 克隆项目:
git clone https://gitcode.com/gh_mirrors/ca/camelinaction22️⃣ 进入第 12 章目录:
cd camelinaction2/chapter123️⃣ 按需运行各模块测试(每个子模块都有独立 README.md 说明):
cd xa && mvn test cd idempotent && mvn test总结:如何为你的场景选型?✅
| 你的场景 | 推荐方案 | 参考模块 |
|---|---|---|
| 单资源(仅数据库或仅队列) | 本地事务 | tx-database |
| 队列 + 数据库强一致 | XA 全局事务 | xa |
| 部分步骤需保留(审计场景) | 事务传播 REQUIRES_NEW | propagation |
| 组件不支持事务 | UnitOfWork + onCompletion | uow |
| 消息可能重复投递 | 幂等性消费者 | idempotent |
| 消息丢失排查 | 客户端确认模式 | riderautoparts-partner |
一句话记忆:XA 管"一致性",UnitOfWork 管"补偿",幂等性管"不重复"——三者组合,就是生产级消息系统的可靠性基石。🚀
【免费下载链接】camelinaction2:camel: This project hosts the source code for the examples of the Camel in Action 2nd ed book :closed_book: written by Claus Ibsen and Jonathan Anstey.项目地址: https://gitcode.com/gh_mirrors/ca/camelinaction2
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考