十一.SpringCloudAlibaba极简入门-分布式事务实战seata

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: 在单体应用中通常情况下只有一个数据库(单数据源),集成事务是一个非常容易的工作。Spring对事务做了很好的管理,我们只需要通过简单的注解@Transactional就可以完成本地事务管理。但是在微服务项目中事务的管理变得困难,因为微服务项目往往有很多的数据库组成,如果在一个业务中涉及到了对多个微服务以及多个数据库的写操作(跨多个数据源),那么要如何才能保证多个数据库组件的读写一致呢?即:同时操作两个数据库,数据库A写操作成功过,数据库B写操作失败要怎么样让数据库A的写操作回滚?很显然用本地事务管理是不能实现了。我们知道,虽然Spring对事务做了很好的管理和封装,但是最终都是调用数据

老鸟飞过,只做学习使用,欢迎交流 .

注意:本文章是基于前面的章节进行开展

1.理解Seata

1.1.为什么要分布式事务

在单体应用中通常情况下只有一个数据库(单数据源),集成事务是一个非常容易的工作。Spring对事务做了很好的管理,我们只需要通过简单的注解@Transactional就可以完成本地事务管理。

但是在微服务项目中事务的管理变得困难,因为微服务项目往往有很多的数据库组成,如果在一个业务中涉及到了对多个微服务以及多个数据库的写操作(跨多个数据源),那么要如何才能保证多个数据库组件的读写一致呢?即:同时操作两个数据库,数据库A写操作成功过,数据库B写操作失败要怎么样让数据库A的写操作回滚?很显然用本地事务管理是不能实现了。

我们知道,虽然Spring对事务做了很好的管理和封装,但是最终都是调用数据库的事务命令完成事务管理,那在微服务中是跨多个数据库的写操作,如果想要实现事务管理(要么都成功,要么到失败)就需要协调多个数据库的提交,回滚等操作,这就需要使用分布式事务框架来实现。

1.2.什么是Seata

eata(Simple Extensible Autonomous Transaction Architecture) 是 阿里巴巴开源的分布式事务中间件,致力于提供高性能,零入侵和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

Seata 的设计思路是将一个分布式事务可以理解成一个全局事务,下面挂了若干个分支事务,而一个分支事务是一个满足 ACID 的本地事务,因此我们可以操作分布式事务像操作本地事务一样。

1.3.Seata的工作方式

Seata相关术语

  • Transcation ID(XID) : 由事务协调者创建的全局唯一的事务ID

  • Transaction Coordinator(TC) - 事务协调器:一个独立运行的组件,负责维护全局事务的运行状态,负责根据TM的指令协调并驱动全局事务的提交或回滚,负责向资源管理器发起事务提交,回滚指令。

  • Transaction Manager - 事务管理器 :控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议,通知TC提交或者回滚事务。

  • Resource Manager(RM) - 资源管理器: 控制分支事务,负责分支事务注册、负责向TC汇报分支事务状态,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚

Seata全局事务执行流程如下

在这里插入图片描述

角色说明

TC事务协调者是一个独立的服务,由Seata提供。

Business是我们的微服务作为业务主服务,它也是事务管理者TM(通过导入TM的jar来集成)

Business通过远程调用Order服务下单和调用Account服务修改账户,Order和Account作为资源管理器RM(导入RM的jar来集成)

正常执行流程

  1. 系统启动TM事务管理者以及RM资源管理者需要向事务协调器进行提交注册,可以看做是一种初始化。
  2. 在Bussiness的业务主方法上我们需要打上@GlobalTransationl注解,通过这个注解,事务管理器(TM)向事务协调者(TC)申请开启一个全局分布式事务,事务协调者创建全局事务后返回全局唯一的 XID,这个XID 会在涉及微服务的整个全局事务的上下文中进行传播。
  3. 业务开始,Bussiness通过Feign调用Order,并传递全局事务XID,Order在做写操作的时候,RM资源管理器 (Order集成了RM)向事务 协调器TC 注册本地分支事务,该分支事务归属于拥有相同 XID 的全局事务,同时事务协调者TC会返回一个分支事务ID:“branchId” 。说明一下:Seate通过代理DataSource向TC发起分子事务注册的。
  4. 这个时候Order会正常的写数据库,然后会写入一个undo log(这个日志文件记录了数据库修改前的数据,用来做回滚),然后提交分支事务(注意,分支事务已经提交了) , 最后向TC上报事务处理状态。当然Account做的事情和Order是相同的。
  5. Order和Account都调用完成,代码回到Business,这时TM事务管理器向TC事务协调者发起全局事务提交请求,TC向RM事务分支发起事务提交请求,RM(Order, Account)直接删除到undo log日志文件即可,因为之前已经提交了本地事务

异常执行流程
在这里插入图片描述

前面2个步骤都跟上面一样,这里省略 , 在第 3 步的时候,Order在可能因为某种原因本地分支事务提交失败了,那么RM会向TC上报一个失败的事务状态

在第 4 步 ,这个时候代码回到Business,这时TM事务管理器会向TC事务协调者发起全局事务回滚请求,TC向RM事务分支发起事务回滚请求,RM(Order, Account)收到回滚指令,然后会解析undo log,指向反向操作,把数据还原到修改之前,删除undo log,提交本地事务。

2.Seata事务协调器安装

2.1.下载Seata

下载Seata-server :https://github.com/seata/seata/releases , 然后解压Seata,我们需要对Seata做一些配置,比如事务日志基于DB,使用Nacos作为服务注册发现组件。

2.2.基于DB的事务日志

修改配置文件 config/file.config 修改事务日志存储为DB : store修改为db
在这里插入图片描述
创建数据库 seata ,并导入相关的事务日志表的SQL脚本
在这里插入图片描述
数据库如下

在这里插入图片描述

2.3.注册中心配置为Nacos

  • 修改配置 confg/registry.conf , 把注册中心配置为Nacos

在这里插入图片描述

2.4.启动Seata

执行 bin/seata-server.bat 启动
在这里插入图片描述

3.分布式事务实战

准备2个微服务,订单,账户,每个微服务有自己的数据库,自己创建表,然后演示一个下单,修改账户的案例来演示分布式事务。

3.1.订单微服务

准备工作:搭建好基本服务,链接上自己的数据库,创建好domain,server,mapper,controller等组件 , 做好服务注册发现等相关工作,然后导入Seata依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <exclusions>
        <exclusion>
            <artifactId>seata-all</artifactId>
            <groupId>io.seata</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-all</artifactId>
    <version>0.9.0</version>
</dependency>

application.yml配置

server:
  port: 1020
spring:
  application:
    name: order-server
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    alibaba:
      seata:
        tx-service-group: default-test #事务组要和seata/config/file.conf中的事务组对应 ,key匹配
  datasource:
    url: jdbc:mysql:///seata-order?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: admin
    driver-class-name: com.mysql.jdbc.Driver
    #type: com.alibaba.druid.pool.DruidDataSource
mybatis:
  mapper-locations: classpath:mapper/*Mapper.xml

把seata/config中的配置文件 file.conf 和 registry.conf 拷贝到resources中 , resources/file.conf 如下

service {
   
   
  #vgroup->rgroup
  vgroup_mapping.default-test = "default" #修改事务分组名称 ,
  ...

store {
   
   
  ## store mode: file、db
  #mode = "file"
  mode = "db"  #事务日志存储到db
  ...

 ## database store
db {
   
   
    datasource = "dbcp"
    ## mysql/oracle/h2/oceanbase etc.
    db-type = "mysql"
    driver-class-name = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://127.0.0.1:3306/seata"    
    user = "root"
    password = "admin"
    min-conn = 1
    max-conn = 3
    global.table = "global_table"
    branch.table = "branch_table"
    lock-table = "lock_table"
    query-limit = 100
  }
}

resources/registry.conf 如下

registry {
   
   
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  #type = "file"
  type = "nacos" #使用Nacos作为注册中心

  nacos {
   
   
    serverAddr = "localhost:8848"  #nacos的地址
    namespace = ""
    cluster = "default"
  }

需要注意的是:application.yml中的 tx-service-group: default-test 的值和 resources/file.conf 中的vgroup_mapping.default-test = "default" 的key对应 。

配置DataSource , 把DataSource代理给Seata , Seata就是通过DataSourceProxy来向TC发起事务注册,其实我们这里的Order即使TM,也是RM。

//使用seata对DataSource进行代理
@Configuration
public class DataSourceProxyConfig {
   
   

    //mapper.xml路径
    @Value("${mybatis.mapper-locations}")
    private String mapperLocations;

    //手动配置bean
    @Bean
    @ConfigurationProperties("spring.datasource")
    public DataSource druidDataSource(){
   
   
        return new DruidDataSource();
    }

    @Bean
    public SqlSessionFactory sessionFactory(DataSourceProxy dataSourceProxy) throws Exception {
   
   
        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSourceProxy);
        sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        //事务管理工厂
        sessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
        return sessionFactoryBean.getObject();
    }

    @Bean
    public DataSourceProxy dataSource() {
   
   
        return new DataSourceProxy(druidDataSource());
    }
}

主配置类排除DataSource的自动配置 ,因为上面已经手动配置了DataSource

//服务注册与发现
//排除DataSource自动创建
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@MapperScan("org.example.mapper")
public class OrderServerApplication1020 {
   
   
//...

在发起调用的主业务方法上贴注解 : @GlobalTransactional 开启全局事务


   @GlobalTransactional //全局事务标签
   @Transactional //本地事务标签
   @Override
   public void createOrder() {
   
   
           //简单模拟下单
       orderMapper.createOrder();
       //简单模拟修改账户
       accountService.updateAccount();
   }

最后记得在订单的数据库插入一个表,可以从Seata 的安装目录的config目录中找到 db_undo_log.sql 脚本,该表的作用是Seata在处理事务的时候做事务日志和回滚用

CREATE TABLE `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) NOT NULL,
  `log_created` datetime NOT NULL,
  `log_modified` datetime NOT NULL,
  `ext` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

3.2.账户服务

同订单服务一样的做法,只是 accountService 不需要打 @GlobalTransactional

@Transactional
@Override
public void createOrder() {
   
   
        //简单模拟下单
    orderMapper.createOrder();
    //简单模拟修改账户
    accountService.updateAccount();
}

3.3.测试

启动Order和Account ,向Order发起下单请求,那么Order和Account的数据库都会修改成功,然后在accountService.updateAccount()业务逻辑中抛出异常,再次访问,那么所有的数据都不会修改成功,事务统一回滚。

相关文章
|
27天前
|
Java 数据库
在Java中使用Seata框架实现分布式事务的详细步骤
通过以上步骤,利用 Seata 框架可以实现较为简单的分布式事务处理。在实际应用中,还需要根据具体业务需求进行更详细的配置和处理。同时,要注意处理各种异常情况,以确保分布式事务的正确执行。
|
1天前
|
数据管理 API 调度
鸿蒙HarmonyOS应用开发 | 探索 HarmonyOS Next-从开发到实战掌握 HarmonyOS Next 的分布式能力
HarmonyOS Next 是华为新一代操作系统,专注于分布式技术的深度应用与生态融合。本文通过技术特点、应用场景及实战案例,全面解析其核心技术架构与开发流程。重点介绍分布式软总线2.0、数据管理、任务调度等升级特性,并提供基于 ArkTS 的原生开发支持。通过开发跨设备协同音乐播放应用,展示分布式能力的实际应用,涵盖项目配置、主界面设计、分布式服务实现及部署调试步骤。此外,深入分析分布式数据同步原理、任务调度优化及常见问题解决方案,帮助开发者掌握 HarmonyOS Next 的核心技术和实战技巧。
107 76
鸿蒙HarmonyOS应用开发 | 探索 HarmonyOS Next-从开发到实战掌握 HarmonyOS Next 的分布式能力
|
1天前
|
物联网 调度 vr&ar
鸿蒙HarmonyOS应用开发 |鸿蒙技术分享HarmonyOS Next 深度解析:分布式能力与跨设备协作实战
鸿蒙技术分享:HarmonyOS Next 深度解析 随着万物互联时代的到来,华为发布的 HarmonyOS Next 在技术架构和生态体验上实现了重大升级。本文从技术架构、生态优势和开发实践三方面深入探讨其特点,并通过跨设备笔记应用实战案例,展示其强大的分布式能力和多设备协作功能。核心亮点包括新一代微内核架构、统一开发语言 ArkTS 和多模态交互支持。开发者可借助 DevEco Studio 4.0 快速上手,体验高效、灵活的开发过程。 239个字符
131 13
鸿蒙HarmonyOS应用开发 |鸿蒙技术分享HarmonyOS Next 深度解析:分布式能力与跨设备协作实战
|
8天前
|
NoSQL Java Redis
秒杀抢购场景下实战JVM级别锁与分布式锁
在电商系统中,秒杀抢购活动是一种常见的营销手段。它通过设定极低的价格和有限的商品数量,吸引大量用户在特定时间点抢购,从而迅速增加销量、提升品牌曝光度和用户活跃度。然而,这种活动也对系统的性能和稳定性提出了极高的要求。特别是在秒杀开始的瞬间,系统需要处理海量的并发请求,同时确保数据的准确性和一致性。 为了解决这些问题,系统开发者们引入了锁机制。锁机制是一种用于控制对共享资源的并发访问的技术,它能够确保在同一时间只有一个进程或线程能够操作某个资源,从而避免数据不一致或冲突。在秒杀抢购场景下,锁机制显得尤为重要,它能够保证商品库存的扣减操作是原子性的,避免出现超卖或数据不一致的情况。
42 10
|
14天前
|
消息中间件 SQL 中间件
大厂都在用的分布式事务方案,Seata+RocketMQ带你打破10万QPS瓶颈
分布式事务涉及跨多个数据库或服务的操作,确保数据一致性。本地事务通过数据库直接支持ACID特性,而分布式事务则需解决跨服务协调难、高并发压力及性能与一致性权衡等问题。常见的解决方案包括两阶段提交(2PC)、Seata提供的AT和TCC模式、以及基于消息队列的最终一致性方案。这些方法各有优劣,适用于不同业务场景,选择合适的方案需综合考虑业务需求、系统规模和技术团队能力。
103 7
|
26天前
|
存储 Java 关系型数据库
在Spring Boot中整合Seata框架实现分布式事务
可以在 Spring Boot 中成功整合 Seata 框架,实现分布式事务的管理和处理。在实际应用中,还需要根据具体的业务需求和技术架构进行进一步的优化和调整。同时,要注意处理各种可能出现的问题,以保障分布式事务的顺利执行。
46 6
|
26天前
|
数据库
如何在Seata框架中配置分布式事务的隔离级别?
总的来说,配置分布式事务的隔离级别是实现分布式事务管理的重要环节之一,需要认真对待和仔细调整,以满足业务的需求和性能要求。你还可以进一步深入研究和实践 Seata 框架的配置和使用,以更好地应对各种分布式事务场景的挑战。
28 6
|
24天前
|
消息中间件 运维 数据库
Seata框架和其他分布式事务框架有什么区别
Seata框架和其他分布式事务框架有什么区别
24 1
|
2月前
|
消息中间件 关系型数据库 Java
‘分布式事务‘ 圣经:从入门到精通,架构师尼恩最新、最全详解 (50+图文4万字全面总结 )
本文 是 基于尼恩之前写的一篇 分布式事务的文章 升级而来 , 尼恩之前写的 分布式事务的文章, 在全网阅读量 100万次以上 , 被很多培训机构 作为 顶级教程。 此文修改了 老版本的 一个大bug , 大家不要再看老版本啦。
|
2月前
|
NoSQL Java Redis
开发实战:使用Redisson实现分布式延时消息,订单30分钟关闭的另外一种实现!
本文详细介绍了 Redisson 延迟队列(DelayedQueue)的实现原理,包括基本使用、内部数据结构、基本流程、发送和获取延时消息以及初始化延时队列等内容。文章通过代码示例和流程图,逐步解析了延迟消息的发送、接收及处理机制,帮助读者深入了解 Redisson 延迟队列的工作原理。

热门文章

最新文章