Spring Cloud异步场景分布式事务怎样做?试试RocketMQ

一、背景
在微服务架构中,我们常常使用异步化的手段来提升系统的 吞吐量 和 解耦 上下游,而构建异步架构最常用的手段就是使用 消息队列(MQ),那异步架构怎样才能实现数据一致性呢?本文主要介绍如何使用RocketMQ的事务消息来解决一致性问题。
RocketMQ 是阿里巴巴开源的分布式消息中间件,目前已成为 Apache 的顶级项目。历经多次天猫双十一海量消息考验,具有高性能、低延时和高可靠等特性
PS:同步场景怎样保证一致性?请看文章《Spring Cloud同步场景分布式事务怎样做?试试Seata》
二、MQ选型
可以看到在 业务处理 方面来说 RocketMQ 优于其他对手,而且原生支持 事务消息

PS:业务系统用的是其他 MQ 产品但是又需要 事务消息 怎么办?学习原理自己开发实现!
三、什么是事务消息
例如下图的场景:生成订单记录 -> MQ -> 增加积分

我们是应该先 创建订单记录,还是先 发送MQ消息 呢?
先发送MQ消息:这个明显是不行的,因为如果消息发送成功,而订单创建失败的话是没办法把消息收回来的
先创建订单记录:如果订单创建成功后MQ消息发送失败 抛出异常,因为两个操作都在本地事务中所以订单数据是可以 回滚 的
上面的 方式二 看似没问题,但是 网络是不可靠的!如果 MQ 的响应因为网络原因没有收到,所以在面对不确定的结果只好进行回滚;但是 MQ 端又确实是收到了这条消息的,只是回给客户端的 响应丢失 了!
所以 事务消息 就是用来保证 本地事务 与 MQ消息发送 的原子性!
四、RocketMQ事务消息原理

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

五、异步架构一致性实现思路
从上面的原理可以发现 事务消息 仅仅只是保证本地事务和MQ消息发送形成整体的 原子性,而投递到MQ服务器后,并无法保证消费者一定能消费成功!
如果 消费端消费失败 后的处理方式,建议是记录异常信息然后 人工处理,并不建议回滚上游服务的数据(因为两者是 解耦 的,而且 复杂度 太高)
我们可以利用 MQ 的两个特性 重试 和 死信队列 来协助消费端处理:
- 消费失败后进行一定次数的
重试 - 重试后也失败的话该消息丢进
死信队列里 - 另外起一个线程监听消费
死信队列里的消息,记录日志并且预警!
因为有 重试 所以消费者需要实现 幂等性
六、分布式事务场景样例
下面就用刚刚提到的场景:生成订单记录 -> MQ -> 增加积分;来简单讲一下 Spring Cloud 中应该怎么做,详细代码请 下载demo 查看。
PS:怎样安装部署RocketMQ可以参考《Apache RocketMQ 消息队列部署与可视化界面安装》
6.1. 引入依赖
使用 spring-cloud-stream 框架来访问 RocketMQ

Spring Cloud Stream 是一个构建消息驱动的框架,通过抽象的定义实现应用与MQ消息队列之间的解耦,目前支持
RabbitMQ、kafka和RocketMQ
6.2. 开启事务消息
消息生产者需要添加 transactional: true 开启 事务消息

6.3. 订单服务发送half消息

因为开启了
事务消息所以这里发送的是half消息对于消费端是不可见的
6.4. 订单服务监听half消息
使用 @RocketMQTransactionListener 注解监听 半消息,并实现 RocketMQLocalTransactionListener 接口,该接口有两个方法
- executeLocalTransaction:用于提交本地事务
- checkLocalTransaction:用于事务回查

如果提交事务消息失败,需等待约1分钟左右 事务回查 方法才会被调用
6.5. 积分服务消费消息

注意:因为有
重试,这里如果是真实的业务需要自行实现幂等性
6.6. 消费死信队列预警

监听并消费死信队列中的消息,用于记录错误日志,并且预警通知运维人员等
6.7. 测试用例
demo中提供了3个接口分别测试不同的场景:
- 事务成功
http://localhost:11002/success
流程如下:- 订单创建 成功
- 提交事务消息 成功
- 消费消息增加积分 成功
- 订单创建成功但提交事务消息失败
http://localhost:11002/produceError
流程如下:- 订单创建 成功
- 提交事务消息 失败
- 事务回查(等待1分钟左右) 成功
- 提交事务消息 成功
- 消费消息增加积分 成功
- 消费消息失败
http://localhost:11002/consumeError
流程如下:- 订单创建 成功
- 提交事务消息 成功
- 消费消息增加积分 失败
- 重试消费消息 失败
- 进入死信队列 成功
- 消费死信队列的消息 成功
- 记录日志并发出预警 成功
七、demo下载地址
推荐阅读
- 日志排查问题困难?分布式日志链路跟踪来帮你
- zuul集成Sentinel最新的网关流控组件
- 阿里注册中心Nacos生产部署方案
- Spring Boot自定义配置项在IDE里面实现自动提示
- Spring Cloud Zuul的动态路由怎样做?集成Nacos实现很简单
- Spring Cloud开发人员如何解决服务冲突和实例乱窜?
- Spring Cloud同步场景分布式事务怎样做?试试Seata
扫码关注有惊喜!

Spring Cloud异步场景分布式事务怎样做?试试RocketMQ的更多相关文章
- Spring Cloud同步场景分布式事务怎样做?试试Seata
一.概述 在微服务架构下,虽然我们会尽量避免分布式事务,但是只要业务复杂的情况下这是一个绕不开的问题,如何保证业务数据一致性呢?本文主要介绍同步场景下使用Seata的AT模式来解决一致性问题. Sea ...
- Spring Cloud Gateway的动态路由怎样做?集成Nacos实现很简单
一.说明 网关的核心概念就是路由配置和路由规则,而作为所有请求流量的入口,在实际生产环境中为了保证高可靠和高可用,是尽量要避免重启的,所以实现动态路由是非常有必要的:本文主要介绍 Spring Clo ...
- Spring Cloud第九篇 | 分布式服务跟踪Sleuth
本文是Spring Cloud专栏的第九篇文章,了解前八篇文章内容有助于更好的理解本文: Spring Cloud第一篇 | Spring Cloud前言及其常用组件介绍概览 Spring Cl ...
- 【Spring Cloud】Spring Cloud Config 实现分布式配置中心
Spring Cloud Config 实现分布式配置中心 一.分布式配置中心 分布式系统中,往往拥有大量的服务应用,而每个应用程序都需要有对应的配置文件来协助完成服务环境初始化.运行.因此生产了大量 ...
- Spring+JTA+Atomikos+mybatis分布式事务管理
我们平时的工作中用到的Spring事务管理是管理一个数据源的.但是如果对多个数据源进行事务管理该怎么办呢?我们可以用JTA和Atomikos结合Spring来实现一个分布式事务管理的功能.了解JTA可 ...
- Spring Cloud(九):分布式配置中心和消息总线
我们在Spring Cloud(七):使用SVN存储分布式配置中心文件和实现refresh中讲到,如果需要客户端获取到最新的配置信息需要执行refresh,我们可以利用webhook的机制每次提交代码 ...
- 事务隔离级别与传播机制,spring+mybatis+atomikos实现分布式事务管理
1.事务的定义:事务是指多个操作单元组成的合集,多个单元操作是整体不可分割的,要么都操作不成功,要么都成功.其必须遵循四个原则(ACID). 原子性(Atomicity):即事务是不可分割的最小工作单 ...
- Spring事务隔离级别与传播机制详解,spring+mybatis+atomikos实现分布式事务管理
原创说明:本文为本人原创作品,绝非他处转载,转账请注明出处 1.事务的定义:事务是指多个操作单元组成的合集,多个单元操作是整体不可分割的,要么都操作不成功,要么都成功.其必须遵循四个原则(ACID). ...
- 构建Spring Cloud微服务分布式云架构
大型企业分布式微服务云架构服务组件 实现模块化.微服务化.原子化.灰度发布.持续集成 commonservice zipkinSpring 日志收集工具包,封装了Dapper和log-based追踪以 ...
随机推荐
- 解决MobaXterm-SSH中文乱码问题
一般情况不用修改服务器字符集(linux或unix服务器字符集一般不会设置错误). 1.首先用命令查看当前系统的LANG是什么: >locale LANG=en_US LC_COLLATE=&q ...
- Java网络编程 -- BIO 阻塞式网络编程
阻塞IO的含义 阻塞(blocking)IO :阻塞是指结果返回之前,线程会被挂起,函数只有在得到结果之后(或超时)才会返回 非阻塞(non-blocking)IO :非阻塞和阻塞的概念相对应,指在不 ...
- c语言推箱子 扫雷项目
推箱子 两关的推箱子用到一个三维数组 用到的图片数据如下 https://pan.baidu.com/s/1IDE4GQLo46cxNywDqwxmlQ 密码:jdel 代码如下: #include& ...
- C#高级语法之泛型、泛型约束,类型安全、逆变和协变(思想原理)
一.为什么使用泛型? 泛型其实就是一个不确定的类型,可以用在类和方法上,泛型在声明期间没有明确的定义类型,编译完成之后会生成一个占位符,只有在调用者调用时,传入指定的类型,才会用确切的类型将占位符替换 ...
- 阿里云部署 Flask + WSGI + Nginx 转载详解
我采用的部署方案是: Web 服务器采用 uwsgi host Flask 用 Supervisor 引用 uwsgi 作常规启动服务 基于 Nginx 作反向代理 首先, 阿里云服务器可以通过 SS ...
- 章节十六、5-TestNG高级功能--Part2
一.测试用例的依赖关系--->(dependsOnMethods = {"依赖方法名"}) 1.在实现自动化的过程中,有些测试用例必须在其它测试用例执行之后才能运行,两者之间 ...
- css3的@media
都知道bootstrap响应式布局很酷,但是是怎么实现的呢?其官网首页有提到这一切的功劳都是来自于CSS 媒体查询(Media Query). 使用 @media 查询,你可以针对不同的媒体类型定义不 ...
- JAVA实现读取图片
话不读说 直接上代码 package cn.kgc.ssm.common; import java.io.*; /** * @author * @create 2019-08-15 9:36 **/ ...
- Aizu-2224Save your cats并查集+最小生成树
Save your cats 题意:存在n个点,有m条边( input中读入的是 边的端点,要先转化为边的长度 ),做一个最小生成树,使得要去除的边的长度总和最小: 思路:利用并查集和求最小生成树的方 ...
- 51nod 1257 背包问题 V3(这不是背包问题是二分)
题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1257 题解:不能按照单位价值贪心,不然连样例都过不了 要求的 ...
