springcloud整合seata实现分布式事务

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
日志服务 SLS,月写入数据量 50GB 1个月
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: springcloud整合seata实现分布式事务

概览

本篇文章,根据官网代码demo演示而来

springcloud整合seata实现分布式事务 DEMO


操作步骤如下:

  • 1.seata-server端,修改server配置
  • 2.client端(你自己的项目),引入配置文件,修改配置文件
  • 3.数据源代理设置
  • 4.创建数据库表
  • 5.启动注册中心(eureka),启动server,启动client(包括订单服务,库存服务、账户服务)


1.此demo技术选型及版本信息

  • 注册中心:eureka 2.1.2
  • 服务间调用:feign 2.1.2
  • 持久层:mybatis 3.5.0
  • 数据库:mysql 5.7.20
  • Springboot:2.1.8.RELEASE(Greenwich.SR2)
  • Springcloud:2.1.2
  • jdk:1.8
  • seata:1.0 (最新稳定版)


2.demo概况

demo分为四个项目,单独启动。

  • eureka: 注册中心(http://localhost:8761/eureka/)
  • order: 订单服务,用户下单后,会创建一个订单添加在order数据库,同时会扣减库存storage,扣减账户account;
  • storage: 库存服务,用户扣减库存;
  • account: 账户服务,用于扣减账户余额;

order服务关键代码如下:

    @Override
    @GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class) //此注解开启全局事务
    public void create(Order order) {
        //本地方法 创建订单
        orderDao.create(order);
        //远程方法 扣减库存
        storageApi.decrease(order.getProductId(),order.getCount());
        //远程方法 扣减账户余额  可在accountServiceImpl中模拟异常
        accountApi.decrease(order.getUserId(),order.getMoney());
    }


3.创建业务数据库

  • seata_order:存储订单的数据库;
  • seata_storage:存储库存的数据库;
  • seata_account:存储账户信息的数据库
  • seata_server: 如果使用db模式存储事务日志,需要我们要创建三张表:global_table,branch_table,lock_table
  • 所以在每个业务库中,还要创建undo_log表,建表sql在/conf/db_undo_log.sql中。


4.seata server端配置信息修改

seata-server中,/conf目录下,有两个配置文件,需要结合自己的情况来修改:

4.1.单个seata server端配置信息修改

1.file.conf

原始内容

service {
  #transaction service group mapping
  vgroup_mapping.my_test_tx_group = "default"
  #only support when registry.type=file, please don't set multiple addresses
  default.grouplist = "127.0.0.1:8091"
  #disable seata
  disableGlobalTransaction = false
}
## transaction log store, only used in seata-server
store {
  ## store mode: file、db
  mode = "file"
  ## file store property
  file {
    ## store location dir
    dir = "sessionStore"
  }
  ## database store property
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
    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 = "mysql"
    password = "mysql"
  }
}

里面有事务组配置,锁配置,事务日志存储等相关配置信息,由于此demo使用db存储事务信息,我们这里要修改store中的配置:

## transaction log store
store {
  ## store mode: file、db
  mode = "db"   修改这里,表明事务信息用db存储
  ## file store 当mode=db时,此部分配置就不生效了,这是mode=file的配置
  file {
    dir = "sessionStore"
    # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
    max-branch-session-size = 16384
    # globe session size , if exceeded throws exceptions
    max-global-session-size = 512
    # file buffer size , if exceeded allocate new buffer
    file-write-buffer-cache-size = 16384
    # when recover batch read size
    session.reload.read_size = 100
    # async, sync
    flush-disk-mode = async
  }
  ## database store  mode=db时,事务日志存储会存储在这个配置的数据库里
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
    datasource = "dbcp"
    ## mysql/oracle/h2/oceanbase etc.
    db-type = "mysql"
    driver-class-name = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://116.62.62.26/seat-server"  修改这里
    user = "root"  修改这里
    password = "root"  修改这里
    min-conn = 1
    max-conn = 3
    global.table = "global_table"
    branch.table = "branch_table"
    lock-table = "lock_table"
    query-limit = 100
  }
}

由于此demo我们使用db模式存储事务日志,所以,我们要创建三张表:global_table,branch_table,lock_table,建表sql在上面下载的seata-server的/conf/db_store.sql中

由于存储undo_log是在业务库中,所以在每个业务库中,还要创建undo_log表,建表sql在/conf/db_undo_log.sql中。

由于我自定义了事务组名称,所以这里也做了修改:

service {
  #vgroup->rgroup
  vgroup_mapping.fsp_tx_group = "default"  修改这里,fsp_tx_group这个事务组名称是我自定义的,一定要与client端的这个配置一致!否则会报错!
  #only support single node
  default.grouplist = "127.0.0.1:8091"   此配置作用参考:https://blog.csdn.net/weixin_39800144/article/details/100726116
  #degrade current not support
  enableDegrade = false
  #disable
  disable = false
  #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent
  max.commit.retry.timeout = "-1"
  max.rollback.retry.timeout = "-1"
}

其他的可以先使用默认值。


2.registry.conf

registry{}中是注册中心相关配置,config{}中是配置中心相关配置。seata中,注册中心和配置中心是分开实现的,是两个东西。

我们这里用eureka作注册中心,所以,只用修改registry{}中的:

registry {
  # 用eureka作注册中心    
  type = "eureka"  
  eureka {
    # eureka作注册中心地址  
    serviceUrl = "http://localhost:8761/eureka"  
    application = "default"  
    weight = "1"
  }
}

其他的配置可以暂时使用默认值。

如果是在windows下启动seata-server,现在已经完成配置修改了,等eureka启动后,就可以启动seata-server了:执行/bin/seata-server.bat即可。


4.2.seata-server 高可用(要求版本0.9+)

0.9及之前版本,多TC(事务协调者)时,TC会误报异常,此问题0.9之后已经修复,之后的版本应该不会出现此问题。

部署集群,第一台和第二台配置相同,在server端的registry.conf中,注意:

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "eureka"
......
  eureka {
    serviceUrl = "http://localhost:8761/eureka"  //两台tc相同,注册中心的地址
    application = "default" //两台tc相同
    weight = "1"  //权重,截至0.9版本,暂时不支持此参数
  }
 ......

注意上述配置和client的配置要一致,2台和多台情况相同。


5.client端相关配置

1.普通配置

client端的几个服务,都是普通的springboot整合了springCloud组件的正常服务,所以,你需要配置eureka,数据库,mapper扫描等,即使不使用seata,你也需要做,这里不做特殊说明,看代码就好。

2.特殊配置

1.application.yml

以order服务为例,除了常规配置外,这里还要配置下事务组信息:

spring:
    application:
        name: order-server
    cloud:
        alibaba:
            seata:    
                # 这个fsp_tx_group自定义命名很重要,server,client都要保持一致
                tx-service-group: fsp_tx_group  
2.file.conf

自己新建的项目是没有这个配置文件的,copy过来,修改下面配置:

service {
  #vgroup->rgroup
  # 这个fsp_tx_group自定义命名很重要,server,client都要保持一致    
  vgroup_mapping.fsp_tx_group = "default"   
  #only support single node
  default.grouplist = "127.0.0.1:8091"
  #degrade current not support
  enableDegrade = false
  #disable
  disable = false
  disableGlobalTransaction = false
}
3.registry.conf

使用eureka做注册中心,仅需要修改eureka的配置即可:

registry {
  # 使用eureka做注册中心 (还支持file 、nacos 、eureka、redis、zk)
  type = "eureka"
  eureka {
    serviceUrl = "http://localhost:8761/eureka"
    application = "default"
    weight = "1"
  }
}
config {
  # 使用eureka做配置中心 (还支持file、nacos 、apollo、zk)
  type = "file"
  file {
    name = "file.conf"
  }
}

其他的使用默认值就好。


3.数据源代理

这个是要特别注意的地方,seata对数据源做了代理和接管,在每个参与分布式事务的服务中,都要做如下配置:

// 在启动类中取消数据源的自动创建
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@MapperScan("com.fly.demo.dao")
@EnableDiscoveryClient
@EnableFeignClients
public class OrderServerApplication {
  public static void main(String[] args) {
    SpringApplication.run(OrderServerApplication.class, args);
  }
}
/**
 * 数据源代理
 * @author wangzhongxiang
 */
@Configuration
public class DataSourceConfiguration {
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        return druidDataSource;
    }
    @Primary
    @Bean("dataSource")
    public DataSourceProxy dataSource(DataSource druidDataSource){
        return new DataSourceProxy(druidDataSource);
    }
    @Bean
    public SqlSessionFactory sqlSessionFactory(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();
    }
}


6.启动测试(注意先后顺序)

  • 1.启动eureka;
  • 2.启动seata-server;
  • 3.启动order,storage,account服务;
  • 4.访问:http://localhost:8180/order/create?userId=1&productId=1&count=10&money=100

然后可以模拟正常情况,异常情况,超时情况等,观察数据库即可。

2020-02-04 11:02:13.942  INFO 3560 --- [nio-8761-exec-6] c.n.e.registry.AbstractInstanceRegistry  : Registered instance DEFAULT/192.168.115.1:default:8091 with status UP (replication=false)
2020-02-04 11:03:13.427  INFO 3560 --- [nio-8761-exec-3] c.n.e.registry.AbstractInstanceRegistry  : Registered instance ORDER-SERVER/192.168.233.1:order-server:8180 with status UP (replication=false)
2020-02-04 11:04:08.318  INFO 3560 --- [nio-8761-exec-9] c.n.e.registry.AbstractInstanceRegistry  : Registered instance STORAGE-SERVER/192.168.233.1:storage-server:8182 with status UP (replication=false)
2020-02-04 11:04:48.525  INFO 3560 --- [nio-8761-exec-5] c.n.e.registry.AbstractInstanceRegistry  : Registered instance ACCOUNT-SERVER/192.168.233.1:account-server:8181 with status UP (replication=false)


7.测试正常情况

正常情况:

通过我们访问/order下单接口,根据响应的内容我们确定商品已经购买成功

数据库内的账户余额商品库存是有所扣减


8.测试异常情况

在AccountServiceImpl中模拟异常情况,然后可以查看日志

  /**
 * 账户业务实现类
 */
@Service
public class AccountServiceImpl implements AccountService {
    private static final Logger LOGGER = LoggerFactory.getLogger(AccountServiceImpl.class);
    @Autowired
    private AccountDao accountDao;
    /**
     * 扣减账户余额
     */
    @Override
    public void decrease(Long userId, BigDecimal money) {
        LOGGER.info("------->account-service中扣减账户余额开始");
        //模拟超时异常,全局事务回滚
        try {
            Thread.sleep(30*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        accountDao.decrease(userId,money);
        LOGGER.info("------->account-service中扣减账户余额结束");
    }
}

此时我们可以发现下单后数据库数据并没有任何改变 ,事务会发生回滚

但是,当我们在seata-order-service中注释掉@GlobalTransactional来看看没有Seata的分布式事务管理会发生什么情况

由于seata-account-service的超时会导致当库存和账户金额扣减后订单状态并没有设置为已经完成,而且由于远程调用的重试机制,账户余额还会被多次扣减。


参考链接



相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
相关文章
|
2月前
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
486 3
|
8天前
|
负载均衡 Java API
《深入理解Spring》Spring Cloud 构建分布式系统的微服务全家桶
Spring Cloud为微服务架构提供一站式解决方案,涵盖服务注册、配置管理、负载均衡、熔断限流等核心功能,助力开发者构建高可用、易扩展的分布式系统,并持续向云原生演进。
|
7月前
|
SQL
seata是怎么进行分布式事务控制的
seata是怎么进行分布式事务控制的
|
11月前
|
存储 Java 关系型数据库
在Spring Boot中整合Seata框架实现分布式事务
可以在 Spring Boot 中成功整合 Seata 框架,实现分布式事务的管理和处理。在实际应用中,还需要根据具体的业务需求和技术架构进行进一步的优化和调整。同时,要注意处理各种可能出现的问题,以保障分布式事务的顺利执行。
823 53
|
10月前
|
存储 SpringCloudAlibaba Java
【SpringCloud Alibaba系列】一文全面解析Zookeeper安装、常用命令、JavaAPI操作、Watch事件监听、分布式锁、集群搭建、核心理论
一文全面解析Zookeeper安装、常用命令、JavaAPI操作、Watch事件监听、分布式锁、集群搭建、核心理论。
【SpringCloud Alibaba系列】一文全面解析Zookeeper安装、常用命令、JavaAPI操作、Watch事件监听、分布式锁、集群搭建、核心理论
|
9月前
|
Java 关系型数据库 数据库
微服务SpringCloud分布式事务之Seata
SpringCloud+SpringCloudAlibaba的Seata实现分布式事务,步骤超详细,附带视频教程
664 1
|
11月前
|
消息中间件 运维 数据库
Seata框架和其他分布式事务框架有什么区别
Seata框架和其他分布式事务框架有什么区别
231 1
|
2月前
|
存储 负载均衡 NoSQL
【赵渝强老师】Redis Cluster分布式集群
Redis Cluster是Redis的分布式存储解决方案,通过哈希槽(slot)实现数据分片,支持水平扩展,具备高可用性和负载均衡能力,适用于大规模数据场景。
231 2
|
2月前
|
存储 缓存 NoSQL
【📕分布式锁通关指南 12】源码剖析redisson如何利用Redis数据结构实现Semaphore和CountDownLatch
本文解析 Redisson 如何通过 Redis 实现分布式信号量(RSemaphore)与倒数闩(RCountDownLatch),利用 Lua 脚本与原子操作保障分布式环境下的同步控制,帮助开发者更好地理解其原理与应用。
159 6