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. pandas中loc和iloc的使用细节

    1.缘由 前段时间在使用pandas库中的索引和切片的时候,突然就感觉有点懵,赋值和索引的操作总是报错. 网上的很多资料讲的也非常的浅显,而且使用起来非常不顺手. 于是我就找到很多的网上资料,然后自己 ...

  2. JavaScript入门⑧-事件总结大全

    JavaScript入门系列目录 JavaScript入门①-基础知识筑基 JavaScript入门②-函数(1)基础{浅出} JavaScript入门③-函数(2)原理{深入}执行上下文 JavaS ...

  3. 提高python异步效率

    uvloop #Python标准库中提供了asyncio模块,用于支持基于协程的异步编程. #uvloop是 asyncio 中的事件循环的替代方案,替换后可以使得asyncio性能提高.事实上,uv ...

  4. 常用内置模块os sys json

    今日内容回顾 目录 今日内容回顾 os模块 sys模块 json模块 json模块实战 os模块 sys模块 json模块 os模块 os模块主要与代码运行的操作系统打交道 1.创建目录(文件夹) i ...

  5. JavaScript:七大基础数据类型:字符串string

    在JS中,字符串类型的数据,有三种表达方式,但是无一例外都是需要引号扩起来的: 单引号'hello' 双引号"hello" 反引号`hello` 注意: 因为JS没有字符类型,这一 ...

  6. [R语言] R语言PCA分析教程 Principal Component Methods in R

    R语言PCA分析教程 Principal Component Methods in R(代码下载) 主成分分析Principal Component Methods(PCA)允许我们总结和可视化包含由 ...

  7. 基于云基础设施快速部署 RocketMQ 5.0 集群

    本文作者:蔡高扬,Apache RocketMQ Committer, 阿里云智能技术专家. 背景 上图左侧为 RocketMQ 4.x版本集群,属于非切换架构.NameServer 作为无状态节点可 ...

  8. ArcGIS工具 - 按线分割面

    功能说明 在ArcGIS数据处理过程中,有时需要沿线把面要素分割开,可以使用高级编辑中的分割面(Cut Polygon)工具.那么,如果要用线图层分割面图层该怎么办呢?为源GIS为您开发了一个自定义模 ...

  9. SSM框架——MyBatis

    Mybatis 1.Mybatis的使用 1.1给项目导入相关依赖 我这里有几个下载好的依赖包提供给大家 点我下载--junit4.13.2 点我下载--maven3.8.1 点我下载--mybati ...

  10. 【转】C#接口知识

    参考:日常收集 C# 接口知识 (知识全面) 目录 第一节 接口慨述 第二节 定义接口 第三节 定义接口成员 第四节.访问接口 第五节.实现接口 第六节.接口转换 第七节.覆盖虚接口 第一节 接口慨述 ...