Spring Boot单体服务中,添加@Transactional注解就能实现事务。在单体服务中,执行事务都是在同一个数据库下进行。但是随着业务越来越复杂,数据量越来越大会进行分库分表。在微服务场景下,每个服务都有自己的数据库。之前的单体事务无法处理跨库的事务,这个时候就需要使用分布式事务。

前面 Seata 环境搭建 介绍了seata的安装,安装后就需要结合实战项目介绍分布式事务的应用。

版本

  • spring-cloud-starter-alibaba:2.1.1.RELEASE
  • spring-cloud-alibaba-dependencies:2.1.1.RELEASE
  • Seata Server 1.5.2
  • Nacos Server:2.0.1

不同的版本实现可能不太相同,尽量保持版本一致。

效果展现

用户下单的业务逻辑,整个逻辑有两个微服务提供支持:

  • 仓库服务:给对应的商品扣减库存
  • 订单服务:创建订单

用户下单购买商品。

  • 先创建订单,创建订单之后,再扣库存。
  • 如果库存不够,就回滚订单。

搭建服务

项目结构

有三个项目,仓库服务stock、订单服务orderseata服务。seata服务先调用订单服务,然后调用仓库服务。

maven 依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--Spring Cloud Alibaba-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>$Greenwich.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</dependency>

三个项目的依赖都是一样的。

yml 配置

seata需要分别配置配置中心注册中心

  • 配置中心内部放置着各种配置文件,你可以通过自己所需进行获取配置加载到对应的客户端.比如Seata Client端(TM,RM),Seata Server(TC),会去读取全局事务开关,事务会话存储模式等信息。

  • 注册中心可以说是微服务架构中的”通讯录“,它记录了服务和服务地址的映射关系。在分布式架构中,服务会注册到这里,当服务需要调用其它服务时,就到这里找到服务的地址,进行调用.比如Seata Client端(TM,RM),发现Seata Server(TC)集群的地址,彼此通信.

从官网文档找到Nacos 配置中心,在application.yml加入对应的配置:

seata:
config:
type: nacos
nacos:
server-addr: xxxx
group: SEATA_GROUP
namespace: xxxxxx
username: nacos
password: nacos

groupnamespaceNacos控制中心页面可以找到,分别对应下图:

Nacos注册中心同样可以获取如下配置:

seata:
registry:
type: nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
group : SEATA_GROUP
namespace: xxxxx
username: nacos
password: nacos

application对应的是Seata Server配置的服务名。Seata Server将自己封装成一个微服务注册到Nacos注册中心。其他配置和配置中心配置一致。

仓库服务

全部贴代码,文章也比较繁琐。所以使用接口的方式,简单声明代码即可。

仓库扣减库存:

public interface StockService {

    /**
* 减库存
* @param id 商品id
* @param num 扣减数量
*/
void reduceStock(Long id, BigDecimal num);
}

订单服务

创建订单:

public interface OrderService {

    /**
* 创建订单
* @param num 下单数量
* @param price 商品价格
* @param goodsId 商品id
*/
Order create(BigDecimal num,BigDecimal price,Long goodsId);
}

下单操作:

先创建订单,然后扣减库存

@GlobalTransactional(rollbackFor = Exception.class)
public void order(Long goodsId) {
orderService.create(BigDecimal.TEN,BigDecimal.TEN,goodsId);
stockService.reduceStock(goodsId,BigDecimal.TEN);
}

数据不回滚

调用下单服务,库存不够,报错了,但是创建订单不会回滚。

分析原因

事务没有回滚,从官网示例下载到本地运行,却可以回滚,对比两者控制台输出之后,发现官网示例使用了mybatis-plus自动添加了DataSourceProxy数据源代理。

所以需要配置seata代理数据源。

配置数据源代理

配置Druid数据源代理:

@Configuration
public class DataSourceProxyConfig { @Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource(){
return new DruidDataSource();
} @Bean
public DataSourceProxy dataSourceProxy(DataSource dataSource) {
return new DataSourceProxy(dataSource);
} @Bean
public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSourceProxy);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath*:/mapper/*.xml"));
sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
return sqlSessionFactoryBean.getObject();
} }

同时需要关闭数据源自动代理:

seata.enable-auto-data-source-proxy: false

再调用下单接口,仓库报错报错,创建订单回滚。订单服务控制台也提示了数据回滚:

2023-03-01 23:49:32.162  WARN 66509 --- [nio-8040-exec-3] c.a.c.seata.web.SeataHandlerInterceptor  : xid in change during RPC from 192.168.31.115:8091:1864853653168447501 to null
2023-03-01 23:49:33.653 INFO 66509 --- [h_RMROLE_1_3_24] i.s.c.r.p.c.RmBranchRollbackProcessor : rm handle branch rollback process:xid=192.168.31.115:8091:1864853653168447501,branchId=1864853653168447502,branchType=AT,resourceId=jdbc:mysql://34.80.215.211:3306/test,applicationData=null
2023-03-01 23:49:33.653 INFO 66509 --- [h_RMROLE_1_3_24] io.seata.rm.AbstractRMHandler : Branch Rollbacking: 192.168.31.115:8091:1864853653168447501 1864853653168447502 jdbc:mysql://34.80.215.211:3306/test
2023-03-01 23:49:36.005 INFO 66509 --- [h_RMROLE_1_3_24] i.s.r.d.undo.AbstractUndoLogManager : xid 192.168.31.115:8091:1864853653168447501 branch 1864853653168447502, undo_log deleted with GlobalFinished
2023-03-01 23:49:36.393 INFO 66509 --- [h_RMROLE_1_3_24] io.seata.rm.AbstractRMHandler : Branch Rollbacked result: PhaseTwo_Rollbacked

PhaseTwo_Rollbacked表示两阶段回滚。

报错 can not get cluster name

控制台报错:

can not get cluster name in registry config 'service.vgroupMapping.nacos-provide-order-seata-service-group', please make sure registry config correct
can not get cluster name in registry config 'service.vgroupMapping.nacos-provide-order-seata-service-group', please make sure registry config correct
can not get cluster name in registry config 'service.vgroupMapping.nacos-provide-order-seata-service-group', please make sure registry config correct

服务在Nacos配置中心找不到service.vgroupMapping.nacos-provide-order-seata-service-group,这是在找分组事务,官网文档有如下介绍:

配置中心有service.vgroupMapping.default_tx_group配置文件,根据上图讲解,需要在application.yml配置:

seata:
tx-service-group: default_tx_group

配置后还是报错。定位到报错的源码,向上调式源码,在application.yml配置:

spring:
cloud:
alibaba:
seata:
tx-service-group: default_tx_group

总结

本文介绍了Spring Cloud整合分布式事务seta,主要有:

  • 添加相关依赖
  • 配置application.yml配置,主要添加nacos配置中心和注册中心的配置。
  • 实现一个下单服务,先创建订单,然后扣减库存。库存不够,创建订单回滚。

搭建服务完成之后,事务不回滚,对比官网实例项目。需要添加数据源代理,同时关闭据源自动代理。分布式事务就生效了。

控制台一直报错can not get cluster name in registry config,通过调试源码,找到问题的根源,这里学到了通过源码解决问题。以前前段时间一直在看源码,这次通过源码解决问题,努力还是会有收获的

源码

Spring Cloud Alibaba 整合 Seata 实现分布式事务的更多相关文章

  1. Spring Cloud Alibaba 使用Seata解决分布式事务

    为什么会产生分布式事务? 随着业务的快速发展,网站系统往往由单体架构逐渐演变为分布式.微服务架构,而对于数据库则由单机数据库架构向分布式数据库架构转变.此时,我们会将一个大的应用系统拆分为多个可以独立 ...

  2. Spring Cloud Alibaba整合Sentinel

    Spring Cloud Alibaba 整合 Sentinel 一.需求 二.实现步骤 1.下载 sentinel dashboard 2.服务提供者和消费者引入sentinel依赖 3.配置控制台 ...

  3. Spring Cloud Alibaba 整合 Nacos 实现服务配置中心

    在之前的文章 <Nacos 本地单机版部署步骤和使用> 中,大家应该了解了 Nacos 是什么?其中 Nacos 提供了动态配置服务功能 一.Nacos 动态配置服务是什么? 官方是这么说 ...

  4. Spring Cloud Alibaba整合Sentinel流控

    前面我们都是直接通过集成sentinel的依赖,通过编码的方式配置规则等.对于集成到Spring Cloud中阿里已经有了一套开源框架spring-cloud-alibaba,就是用于将一系列的框架成 ...

  5. Spring Cloud Alibaba 介绍及工程准备

    简介 SpringCloud Alibaba是阿里巴巴集团开源的一套微服务架构解决方案. 微服务架构是为了更好的分布式系统开发,将一个应用拆分成多个子应用,每一个服务都是可以独立运行的子工程.其中涵盖 ...

  6. 【Spring Cloud & Alibaba全栈开源项目实战】:SpringBoot整合ELK实现分布式登录日志收集和统计

    一. 前言 其实早前就想计划出这篇文章,但是最近主要精力在完善微服务.系统权限设计.微信小程序和管理前端的功能,不过好在有群里小伙伴的一起帮忙反馈问题,基础版的功能已经差不多,也在此谢过,希望今后大家 ...

  7. Spring Cloud Alibaba学习笔记(1) - 整合Spring Cloud Alibaba

    Spring Cloud Alibaba从孵化器版本毕业:https://github.com/alibaba/spring-cloud-alibaba,记录一下自己学习Spring Cloud Al ...

  8. 【Spring Cloud & Alibaba 实战 | 总结篇】Spring Cloud Gateway + Spring Security OAuth2 + JWT 实现微服务统一认证授权和鉴权

    一. 前言 hi,大家好~ 好久没更文了,期间主要致力于项目的功能升级和问题修复中,经过一年时间的打磨,[有来]终于迎来v2.0版本,相较于v1.x版本主要完善了OAuth2认证授权.鉴权的逻辑,结合 ...

  9. Spring Cloud Alibaba 基础

    Spring Cloud Alibaba 基础 什么是Spring Cloud Alibaba 这里我们不讲解Spring Cloud 和 Spring Cloud Alibaba 的关系,大家自己查 ...

  10. Spring Cloud Alibaba(2)---RestTemplate微服务项目

    RestTemplate微服务项目 前言 因为要运用 Spring Cloud Alibaba 开源组件到分布式项目中,所以这里先搭建一个不通过 Spring Cloud只通过 RestTemplat ...

随机推荐

  1. SQL语句查询关键字 多表查询

    目录 SQL语句查询关键字 select from 编写顺序和查询数据 前期数据准备 编写SQL语句的小技巧 查询关键字之筛选 where 逻辑运算符 not and or between not b ...

  2. 微服务11:熔断、降级的Hystrix实现(附源码)

    微服务1:微服务及其演进史 微服务2:微服务全景架构 微服务3:微服务拆分策略 微服务4:服务注册与发现 微服务5:服务注册与发现(实践篇) 微服务6:通信之网关 微服务7:通信之RPC 微服务8:通 ...

  3. 正确理解和使用JAVA中的字符串常量池

    前言 研究表明,Java堆中对象占据最大比重的就是字符串对象,所以弄清楚字符串知识很重要,本文主要重点聊聊字符串常量池.Java中的字符串常量池是Java堆中的一块特殊存储区域,用于存储字符串.它的实 ...

  4. Azure DevOps 的架构窥探

    工作的缘故,接触 TFS (Team Foundation Server)挺多的,现在改名为 Azure DevOps,分为 可私有化部署版本 Azure DevOps Server,简称ADS,以及 ...

  5. 深入理解 dbt 增量模型

    想要实现数据增量写入数据库,可以选择 dbt 增量模型.通过 dbt 增量模型,我们只用专注于写日增 SQL,不用去关注于如何安全的实现增量写入. dbt 增量模型解决了什么问题 原子性写入:任何情况 ...

  6. MongoDB从入门到实战之.NET Core使用MongoDB开发ToDoList系统(2)-Swagger框架集成

    Swagger是什么? Swagger是一个规范且完整API文档管理框架,可以用于生成.描述和调用可视化的RESTful风格的 Web 服务.Swagger 的目标是对 REST API 定义一个标准 ...

  7. 今天试试NuxtJS

    nuxt可以大幅缩短首屏加载时间 Progressive Web App (PWA) Support 渐进式web应用 简单说 就是让你的web应用表现的就像本地应用一样,可以添加快捷方式 打开的时候 ...

  8. Linux音频采集和在国产化平台中遇到的坑(一)

    Linux音频采集和在国产化平台中遇到的坑(一) 最近在做一个国产化平台的软件项目的开发,是基于国产芯片的银河麒麟系统.其中有一个重要模块,是采集和播放音频数据,播放不用多说了,采集的话,包括采集麦克 ...

  9. MyBatis-Plus生成的id传给前端最后两位变为0

    问题描述: 使用MybatisPlus-Plus插入一条数据,生成的id长这样 1621328019543105539 但是在前端显示的时候id却是这样 1621328019543105500 所以导 ...

  10. Grafana 系列文章(十二):如何使用Loki创建一个用于搜索日志的Grafana仪表板

    概述 创建一个简单的 Grafana 仪表板, 以实现对日志的快速搜索. 有经验的直接用 Grafana 的 Explore 功能就可以了. 但是对于没有经验的人, 他们如何能有一个已经预设了简单的标 ...