热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

springboot手动提交事务_SpringCloud异步场景分布式事务怎样做?试试RocketMQ

作者简介:陶陶老师10年后端工作经验,专注Java、SpringBoot、SpringCloud、分布式系统微服务、中间件等领域。掘金专栏:
c1f823106594f692118e6e4d6e0beec6.png

作者简介:

陶陶老师

10年后端工作经验,

专注Java、SpringBoot、SpringCloud、分布式系统/微服务、中间件等领域。

掘金专栏:zlt2000 的个人主页 - 掘金


一、背景

在微服务架构中,我们常常使用异步化的手段来提升系统的 吞吐量 和 解耦 上下游,而构建异步架构最常用的手段就是使用 消息队列(MQ),那异步架构怎样才能实现数据一致性呢?本文主要介绍如何使用RocketMQ事务消息来解决一致性问题。

RocketMQ 是阿里巴巴开源的分布式消息中间件,目前已成为 Apache 的顶级项目。历经多次天猫双十一海量消息考验,具有高性能、低延时和高可靠等特性

PS:同步场景怎样保证一致性?请看文章《Spring Cloud同步场景分布式事务怎样做?试试Seata》

二、MQ选型

可以看到在 业务处理 方面来说 RocketMQ 优于其他对手,而且原生支持 事务消息

67def791056600394664bdc3743f7b71.png

三、什么是事务消息

例如下图的场景:生成订单记录 -> MQ -> 增加积分

b3182fd241abc68a81057a330a942c3e.png

我们是应该先 创建订单记录,还是先 发送MQ消息 呢?

  1. 先发送MQ消息:这个明显是不行的,因为如果消息发送成功,而订单创建失败的话是没办法把消息收回来的
  2. 先创建订单记录:如果订单创建成功后MQ消息发送失败 抛出异常,因为两个操作都在本地事务中所以订单数据是可以 回滚 的

上面的 方式二 看似没问题,但是 网络是不可靠的!如果 MQ 的响应因为网络原因没有收到,所以在面对不确定的结果只好进行回滚;但是 MQ 端又确实是收到了这条消息的,只是回给客户端的 响应丢失 了!
所以 事务消息 就是用来保证 本地事务 与 MQ消息发送 的原子性!

四、RocketMQ事务消息原理

86cff22b0242564cbb2331769c88181a.png

主要的逻辑分为两个流程:

  • 事务消息发送及提交:
  1. 发送 half消息
  2. MQ服务端 响应消息写入结果
  3. 根据发送结果执行 本地事务(如果写入失败,此时half消息对业务 不可见,本地逻辑不执行)
  4. 根据本地事务状态执行 Commit 或者 Rollback(Commit操作生成消息索引,消息对消费者 可见)
  • 回查流程:
  1. 对于长时间没有 Commit/Rollback 的事务消息(pending 状态的消息),从服务端发起一次 回查
  2. Producer 收到回查消息,检查回查消息对应的 本地事务状态
  3. 根据本地事务状态,重新 Commit 或者 Rollback


逻辑时序图

5e89b27618a79f5a447d753551912400.png

五、异步架构一致性实现思路

从上面的原理可以发现 事务消息 仅仅只是保证本地事务和MQ消息发送形成整体的 原子性,而投递到MQ服务器后,并无法保证消费者一定能消费成功!
如果 消费端消费失败 后的处理方式,建议是记录异常信息然后 人工处理,并不建议回滚上游服务的数据(因为两者是 解耦 的,而且 复杂度 太高)
我们可以利用 MQ 的两个特性 重试死信队列 来协助消费端处理:

  1. 消费失败后进行一定次数的 重试
  2. 重试后也失败的话该消息丢进 死信队列
  3. 另外起一个线程监听消费 死信队列 里的消息,记录日志并且预警!

因为有 重试 所以消费者需要实现幂等性

六、分布式事务场景样例

下面就用刚刚提到的场景:生成订单记录 -> MQ -> 增加积分;来简单讲一下 Spring Cloud 中应该怎么做,详细代码请 下载demo 查看。
PS:怎样安装部署RocketMQ可以参考《Apache RocketMQ 消息队列部署与可视化界面安装》

6.1. 引入依赖

使用 spring-cloud-stream 框架来访问 RocketMQ

89d01138c79180838dcef33961d6130d.png

Spring Cloud Stream 是一个构建消息驱动的框架,通过抽象的定义实现应用与MQ消息队列之间的解耦,目前支持 RabbitMQkafkaRocketMQ

1cb412fe3dea3706109398fe2e9f649a.png

6.2. 开启事务消息

消息生产者需要添加 transactional: true 开启 事务消息

fbc7f48f74fbac3d39c58064c6e9aa09.png

6.3. 订单服务发送half消息

d403787e3f9969b7ff53c2397ddbb177.png
因为开启了 事务消息 所以这里发送的是 half消息 对于消费端是 不可见

6.4. 订单服务监听half消息

使用 @RocketMQTransactionListener 注解监听 半消息,并实现 RocketMQLocalTransactionListener 接口,该接口有两个方法

  • executeLocalTransaction:用于提交本地事务
  • checkLocalTransaction:用于事务回查
0806f1cd36638db7cd461e6524aa5561.png
如果提交事务消息失败,需等待约1分钟左右 事务回查 方法才会被调用

6.5. 积分服务消费消息

26200d6b3142716672d6c474846a2517.png

注意:因为有重试 这里如果是真实的业务需要自行实现 幂等性

6.6. 消费死信队列预警

f372035f0ba7d5efb6e4d051efbeab9a.png
监听并消费死信队列中的消息,用于记录错误日志,并且预警通知运维人员等

6.7. 测试用例

demo中提供了3个接口分别测试不同的场景:

  • 事务成功
    http://localhost:11002/success
    流程如下:
  1. 订单创建 成功
  2. 提交事务消息 成功
  3. 消费消息增加积分 成功
  • 订单创建成功但提交事务消息失败
    http://localhost:11002/produceError
    流程如下:
  1. 订单创建 成功
  2. 提交事务消息 失败
  3. 事务回查(等待1分钟左右) 成功
  4. 提交事务消息 成功
  5. 消费消息增加积分 成功
  • 消费消息失败
    http://localhost:11002/consumeError
    流程如下:
  1. 订单创建 成功
  2. 提交事务消息 成功
  3. 消费消息增加积分 失败
  4. 重试消费消息 失败
  5. 进入死信队列 成功
  6. 消费死信队列的消息 成功
  7. 记录日志并发出预警 成功

七、demo下载地址

https://gitee.com/zlt2000/microservices-platform/tree/master/zlt-demo/rocketmq-demo/rocketmq-transactional

作者简介:

陶陶老师

10年后端工作经验,

专注Java、SpringBoot、SpringCloud、分布式系统/微服务、中间件等领域。

掘金专栏:zlt2000 的个人主页 - 掘金


本文已经获得陶陶老师授权转发,其他人若有兴趣转载,请直接联系作者授权。

更多的学习资料,请看这里:

http://www.jnshu.com/login/1/36856070?source=zhihu-article-taotao



推荐阅读
  • 深入解析Hadoop的核心组件与工作原理
    本文详细介绍了Hadoop的三大核心组件:分布式文件系统HDFS、资源管理器YARN和分布式计算框架MapReduce。通过分析这些组件的工作机制,帮助读者更好地理解Hadoop的架构及其在大数据处理中的应用。 ... [详细]
  • 全面解析运维监控:白盒与黑盒监控及四大黄金指标
    本文深入探讨了白盒和黑盒监控的概念,以及它们在系统监控中的应用。通过详细分析基础监控和业务监控的不同采集方法,结合四个黄金指标的解读,帮助读者更好地理解和实施有效的监控策略。 ... [详细]
  • Spring Cloud学习指南:深入理解微服务架构
    本文介绍了微服务架构的基本概念及其在Spring Cloud中的实现。讨论了微服务架构的主要优势,如简化开发和维护、快速启动、灵活的技术栈选择以及按需扩展的能力。同时,也探讨了微服务架构面临的挑战,包括较高的运维要求、分布式系统的复杂性、接口调整的成本等问题。最后,文章提出了实施微服务时应遵循的设计原则。 ... [详细]
  • 前言无论是对于刚入行工作还是已经工作几年的java开发者来说,面试求职始终是你需要直面的一件事情。首先梳理自己的知识体系,针对性准备,会有事半功倍的效果。我们往往会把重点放在技术上 ... [详细]
  • 深入解析Serverless架构模式
    本文将详细介绍Serverless架构模式的核心概念、工作原理及其优势。通过对比传统架构,探讨Serverless如何简化应用开发与运维流程,并介绍当前主流的Serverless平台。 ... [详细]
  • 并发编程 12—— 任务取消与关闭 之 shutdownNow 的局限性
    Java并发编程实践目录并发编程01——ThreadLocal并发编程02——ConcurrentHashMap并发编程03——阻塞队列和生产者-消费者模式并发编程04——闭锁Co ... [详细]
  • 本文深入探讨了MySQL中常见的面试问题,包括事务隔离级别、存储引擎选择、索引结构及优化等关键知识点。通过详细解析,帮助读者在面对BAT等大厂面试时更加从容。 ... [详细]
  • 本文详细介绍了如何在 Android 中使用值动画(ValueAnimator)来动态调整 ImageView 的高度,并探讨了相关的关键属性和方法,包括图片填充后的高度、原始图片高度、动画变化因子以及布局重置等。 ... [详细]
  • Java多重继承的替代方案及设计考量
    本文探讨了Java为何不支持多重继承,并深入分析了其背后的原理和替代方案。通过理解Java的设计哲学,开发者可以更好地利用接口和其他特性来实现复杂的类结构。 ... [详细]
  • 使用WinForms 实现 RabbitMQ RPC 示例
    本文通过两个WinForms应用程序演示了如何使用RabbitMQ实现远程过程调用(RPC)。一个应用作为客户端发送请求,另一个应用作为服务端处理请求并返回响应。 ... [详细]
  • 本文探讨了现代分布式架构的多样性,包括高并发、多活数据中心、容器化、微服务、高可用性和弹性架构等,并介绍了与这些架构相关的重要管理技术,如DevOps、应用监控和自动化运维。文章还深入分析了分布式系统的核心概念、主要用途及类型,同时对比了单体应用与分布式服务化的优缺点。 ... [详细]
  • 本文探讨了2019年前端技术的发展趋势,包括工具化、配置化和泛前端化等方面,并提供了详细的学习路线和职业规划建议。 ... [详细]
  • Go语言以其简洁的语法和强大的并发处理能力而闻名,特别是在云计算和分布式计算领域有着广泛的应用。本文将深入探讨Go语言中的Channel机制,包括其不同类型及其在实际编程中的应用。 ... [详细]
  • LeetCode 690:计算员工的重要性评分
    在解决LeetCode第690题时,我记录了详细的解题思路和方法。该问题要求根据员工的ID计算其重要性评分,包括直接和间接下属的重要性。本文将深入探讨如何使用哈希表(Map)来高效地实现这一目标。 ... [详细]
  • 本文详细介绍了Java的安装、配置、运行流程以及有效的学习方法,旨在帮助初学者快速上手Java编程。 ... [详细]
author-avatar
郑青青11_875
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有