一、背景

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

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

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

 

二、MQ选型

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

PS:业务系统用的是其他 MQ 产品但是又需要 事务消息 怎么办?学习原理自己开发实现!

 

三、什么是事务消息

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

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

  1. 先发送MQ消息:这个明显是不行的,因为如果消息发送成功,而订单创建失败的话是没办法把消息收回来的

  2. 先创建订单记录:如果订单创建成功后MQ消息发送失败 抛出异常,因为两个操作都在本地事务中所以订单数据是可以 回滚

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

 

四、RocketMQ事务消息原理

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

  • 事务消息发送及提交

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

 
逻辑时序图

 

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

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

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

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

 

六、分布式事务场景样例

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

6.1. 引入依赖

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

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

 

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
    流程如下:

    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

 

推荐阅读

 
扫码关注有惊喜!

Spring Cloud异步场景分布式事务怎样做?试试RocketMQ的更多相关文章

  1. Spring Cloud同步场景分布式事务怎样做?试试Seata

    一.概述 在微服务架构下,虽然我们会尽量避免分布式事务,但是只要业务复杂的情况下这是一个绕不开的问题,如何保证业务数据一致性呢?本文主要介绍同步场景下使用Seata的AT模式来解决一致性问题. Sea ...

  2. Spring Cloud Gateway的动态路由怎样做?集成Nacos实现很简单

    一.说明 网关的核心概念就是路由配置和路由规则,而作为所有请求流量的入口,在实际生产环境中为了保证高可靠和高可用,是尽量要避免重启的,所以实现动态路由是非常有必要的:本文主要介绍 Spring Clo ...

  3. Spring Cloud第九篇 | 分布式服务跟踪Sleuth

    ​ ​本文是Spring Cloud专栏的第九篇文章,了解前八篇文章内容有助于更好的理解本文: Spring Cloud第一篇 | Spring Cloud前言及其常用组件介绍概览 Spring Cl ...

  4. 【Spring Cloud】Spring Cloud Config 实现分布式配置中心

    Spring Cloud Config 实现分布式配置中心 一.分布式配置中心 分布式系统中,往往拥有大量的服务应用,而每个应用程序都需要有对应的配置文件来协助完成服务环境初始化.运行.因此生产了大量 ...

  5. Spring+JTA+Atomikos+mybatis分布式事务管理

    我们平时的工作中用到的Spring事务管理是管理一个数据源的.但是如果对多个数据源进行事务管理该怎么办呢?我们可以用JTA和Atomikos结合Spring来实现一个分布式事务管理的功能.了解JTA可 ...

  6. Spring Cloud(九):分布式配置中心和消息总线

    我们在Spring Cloud(七):使用SVN存储分布式配置中心文件和实现refresh中讲到,如果需要客户端获取到最新的配置信息需要执行refresh,我们可以利用webhook的机制每次提交代码 ...

  7. 事务隔离级别与传播机制,spring+mybatis+atomikos实现分布式事务管理

    1.事务的定义:事务是指多个操作单元组成的合集,多个单元操作是整体不可分割的,要么都操作不成功,要么都成功.其必须遵循四个原则(ACID). 原子性(Atomicity):即事务是不可分割的最小工作单 ...

  8. Spring事务隔离级别与传播机制详解,spring+mybatis+atomikos实现分布式事务管理

    原创说明:本文为本人原创作品,绝非他处转载,转账请注明出处 1.事务的定义:事务是指多个操作单元组成的合集,多个单元操作是整体不可分割的,要么都操作不成功,要么都成功.其必须遵循四个原则(ACID). ...

  9. 构建Spring Cloud微服务分布式云架构

    大型企业分布式微服务云架构服务组件 实现模块化.微服务化.原子化.灰度发布.持续集成 commonservice zipkinSpring 日志收集工具包,封装了Dapper和log-based追踪以 ...

随机推荐

  1. .net测试篇之测试神器Autofixture基本配置一

    系列目录 实际工作中我们需要的数据逻辑万千,千变万化,而AutoFixture默认是按照一定算法随机生成一些假数据,虽然这在多数时候是ok的,但是可能不能满足我们的所有业务场景,有些时候我们需要进行一 ...

  2. pyhton介绍、发展趋势、安装

    pyhton介绍.发展趋势.安装 一.python起源 ​ pyhton的创始人为吉多·范罗苏姆(Gudio van Rossum) (后文简称龟叔) ​ 1. 1989年的圣诞节期间,龟叔为了在阿姆 ...

  3. Log4j2源码分析系列:(一)配置加载

    前言 在实际开发项目中,日志永远是一个绕不开的话题.本系列文章试图以slf4j和log4j2日志体系为例,从源码角度分析日志工作原理. 学习日志框架,首先要熟悉各类日志框架,这里推荐两篇文章,就不再赘 ...

  4. Leetcode之深度优先搜索(DFS)专题-494. 目标和(Target Sum)

    Leetcode之深度优先搜索(DFS)专题-494. 目标和(Target Sum) 深度优先搜索的解题详细介绍,点击 给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S.现在 ...

  5. Swifter.Json 可能是 .Net 平台迄今为止性能最佳的 Json 序列化库【开源】

    Json 简介 Json (JavaScript Object Notation) 是一种轻量级的数据交换格式.它作为目前最欢迎的数据交换格式,也是各大开源贡献者的必争之地,如:阿里爸爸的 fastj ...

  6. HashMap并发下死循环问题解析

    首先小伙伴要明确:死循环问题在JDK 1.8 之前是存在的,JDK 1.8 通过增加loHead和loTail进行了修复. 在JDK 1.7及之前 HashMap在并发情况下导致循环问题,致使服务器c ...

  7. JVM体系结构详解

    每个Java开发人员都知道字节码将由JRE (Java运行时环境)执行.但是很多人不知道JRE是Java Virtual Machine(JVM)的实现,它分析字节码.解释代码并执行代码.作为开发者, ...

  8. Docker笔记(九):网络管理

    Docker的应用运行在容器中,其相互之间或与外部之间是如何通信的,涉及到哪些知识点,本文对相关内容进行整理.因网络这块牵涉的面较多,因此只从日常使用或理解的角度出发,过于专业的就不深入探讨了. 1. ...

  9. IT项目管理入门知识

  10. P2157 [SDOI2009]学校食堂 状压DP

    题意: 排队买饭,时间为前一个人和后一个人的异或和,每个人允许其后面B[i] 个人先买到饭,问最少的总用时. 思路: 用dp[i][j][k] 表示1-i-1已经买好饭了,第i个人后面买饭情况为j,最 ...