【SpringCloud Alibaba】Seta安装、处理分布式事务

🔰 学习视频 🔰

尚硅谷SpringCloud框架开发教程(SpringCloudAlibaba微服务分布式架构丨Spring Cloud)

🔰 项目地址 🔰

https://gitee.com/zqcliudaliuda/cloud2021


一、分布式问题

1对1:单机模式、一台服务器上有服务,另一台服务器上有数据库,两个相对应。
1对N:一台服务器上有服务,多台服务器上有数据库,一个调用多个。
N对N:N台服务器上有不同的服务,每个服务对应自己的一个数据库,如下面这个例子。

用户购买商品的业务逻辑。整个业务逻辑由3个微服务提供支持:
仓储服务:对给定的商品扣除仓储数量。
订单服务:根据采购需求创建订单。
帐户服务:从用户帐户中扣除余额。

在这里插入图片描述

单体应用被拆分成微服务应用,原来的三个模块被拆分成三个独立的应用,分别使用三个独立的数据源,业务操作需要调用三三个服务来完成。此时每个服务内部的数据一致性由本地事务来保证, 但是全局的数据一致性问题没法保证。

一次业务操作需要跨多个数据源或需要跨多个系统进行远程调用,就会产生分布式事务问题

二、Seata简介

Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。

中文官网:https://seata.io/zh-cn/

2.1 术语

🔶 一ID和三组件模型

  • XID(Transaction ID) - 全局事务ID
  • TC (Transaction Coordinator) - 事务协调者
    维护全局和分支事务的状态,驱动全局事务提交或回滚。
  • TM (Transaction Manager) - 事务管理器
    定义全局事务的范围:开始全局事务、提交或回滚全局事务。
  • RM (Resource Manager) - 资源管理器
    管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

🔶 处理过程

  1. TM向TC申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID;
  2. XID在微服务调用链路的上下文中传播; .
  3. RM向TC注册分支事务,将其纳入XID对应全局事务的管辖;
  4. TM向TC发起针对XID的全局提交或回滚决议;
  5. TC调度XID下管辖的全部分支事务完成提交或回滚请求。

在这里插入图片描述

2.2 注解

只需要使用一个@GlobalTransactional注解在业务方法上:
在这里插入图片描述

三、安装

3.1 下载

下载:https://github.com/seata/seata/releases
在这里插入图片描述

下载完成后解压。

3.2 file.conf配置

先备份conf/file.conf再修改。

主要修改:自定义事务组名称+事务日志存储模式为db+数据库连接

自定义事务组名称
在这里插入图片描述
持久化方法改成数据库
在这里插入图片描述
配置数据库
在这里插入图片描述

3.3 数据库

创建数据库
在这里插入图片描述
使用seata-server-0.9.0\seata\conf\db_store.sql创建数据库表格。

创建完成后如下:
在这里插入图片描述

3.4 registry.conf配置

修改注册中心为nacos,且配置注册地址。
在这里插入图片描述

3.5 启动

运行seata-server-0.9.0\seata\bin\seata-server.bat

在nacos可查看到:
在这里插入图片描述

四、分布式业务测试

以下演示要先启动Nacos后再启动Seata。

这里我们会创建三个服务,一个订单服务, 一个库存服务, 一个账户服务。当用户下单时,会在订单服务中创建一个订单, 然后通过远程调用库存服务来扣减下单商品的库存,再通过远程调用账户服务来扣减用户账户里面的余额,最后在订单服务中修改订单状态为已完成。该操作跨越三个数据库,有两次远程调用,很明显会有分布式事务问题。

分布式事务业务说明:下订单——扣库存——减账户余额

4.1 数据库准备

4.1.1 数据库建立

seata_ order:存储订单的数据库;
seata_ storage:存储库存的数据库;
seata_ account:存储账户信息的数据库。

CREATE DATABASE seata_order;
CREATE DATABASE seata_storage;
CREATE DATABASE seata_account;

4.1.2 业务表

seata_order库下建t_order表

USE seata_order;
CREATE TABLE t_order(
    `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    `user_id` BIGINT(11) DEFAULT NULL COMMENT '用户id',
    `product_id` BIGINT(11) DEFAULT NULL COMMENT '产品id',
    `count` INT(11) DEFAULT NULL COMMENT '数量',
    `money` DECIMAL(11,0) DEFAULT NULL COMMENT '金额',
    `status` INT(1) DEFAULT NULL COMMENT '订单状态:0创建中,1已完结'
)ENGINE=INNODB AUTO_INCREMENT=7 CHARSET=utf8;

seata_storage库下建t_storage 表

USE seata_storage;
CREATE TABLE t_storage(
    `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
    `product_id` BIGINT(11) DEFAULT NULL COMMENT '产品id',
    `total` INT(11) DEFAULT NULL COMMENT '总库存',
    `used` INT(11) DEFAULT NULL COMMENT '已用库存',
    `residue` INT(11) DEFAULT NULL COMMENT '剩余库存'
)ENGINE=INNODB AUTO_INCREMENT=7 CHARSET=utf8;
INSERT INTO t_storage(id, product_id, total, used, residue) VALUES(1,1,100,0,100);

seata_account库下建t_account表

USE seata_account;
CREATE TABLE t_account(
    `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
    `user_id` BIGINT(11) DEFAULT NULL COMMENT '用户id',
    `total` DECIMAL(10,0) DEFAULT NULL COMMENT '总额度',
    `used` DECIMAL(10,0) DEFAULT NULL COMMENT '已用额度',
    `residue` DECIMAL(10,0) DEFAULT 0 COMMENT '剩余可用额度'
)ENGINE=INNODB AUTO_INCREMENT=7 CHARSET=utf8;
INSERT INTO t_account(id, user_id, total, used, residue) VALUES(1,1,1000,0,1000);

4.1.3 回滚日志表

找到\seata-server-0.9.0\seata\conf\db_undo_log.sql

在3个库当中分别运行。

4.2 微服务准备

可在Gitee内查看详细代码。

业务需求:下订单——减库存——扣余额——改订单状态

🔶 新建maven项目seata-order-service2001

🔶 POM依赖

<dependencies>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
        <exclusions>
            <exclusion>
                <groupId>seata-all</groupId>
                <artifactId>io.seata</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>io.seata</groupId>
        <artifactId>seata-all</artifactId>
        <version>0.9.0</version>
    </dependency>
    <!-- SpringCloud alibaba sentinel-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    <!-- SpringCloud alibaba nacos-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!-- SpringCloud alibaba sentinel-datasource-nacos 持久化用的-->
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

🔶 application.yaml配置

sserver:
  port: 2001

spring:
  application:
    name: seata-order-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    alibaba:
      seata:
        # 自定义事务组名称需要与seata-server中的配置相对应。
        tx-service-group: my_test_tx_group
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/seata_order
    username: root
    password: 123456

🔶 file.conf配置

复制之前配置好的file.conf到项目的resources目录下。

🔶 registry.conf配置

同样,复制之前配置好的registry.conf到项目的resources目录下。

🔶 其他

编写一些业务类、配置等。

4.3 测试

查看数据库中的当前的状态

SELECT * FROM `seata_order`.`t_order`;
SELECT * FROM `seata_storage`.`t_storage`;
SELECT * FROM `seata_account`.`t_account`;

🔶 正常下单

http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100

{"code":200,"message":"订单创建成功","data":null}

未出任何问题的时候,三个数据库的数据都正常变化,该减少的减少,该增加的增加。

🔶 超时异常,没添加@GlobalTransactional

让账户业务超时20秒,看看效果
在这里插入图片描述

http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100, 报错

当库存和账户金额扣减后,订单状态并没有设置为已经完成,没有从零改为1,而且由于feign的重试机制,账户余额还有可能被多次扣减。

🔶 超时异常,添加@GlobalTransactional

在order业务入口添加@GlobalTransactional注解,当产生异常时进行回滚,保证数据的正确。
在这里插入图片描述

Spring Cloud Alibaba中使用Seata进行分布式事务,需要进行以下步骤: 1. 引入Seata依赖 在pom.xml文件中引入Seata的依赖: ``` <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <version>2.2.0.RELEASE</version> </dependency> ``` 2. 配置Seata 在application.yml文件中配置Seata的相关参数,包括: - 服务端配置 ``` spring: cloud: alibaba: seata: tx-service-group: my_test_tx_group # Seata事务组名称 service: vgroup-mapping: my_test_tx_group: default # Seata服务组名称 group-id: default # Seata服务分组ID config: type: file # Seata配置类型 file: name: file.conf # Seata配置文件名称 path: /seata/config # Seata配置文件路径 ``` - 客户端配置 ``` mybatis: configuration: # 启用二级缓存 cache-enabled: true # 数据源配置 type-aliases-package: com.example.demo.entity mapper-locations: classpath:mapper/*.xml configuration-properties: # 自动驼峰转换 mapUnderscoreToCamelCase: true # 数据库连接池配置 druid: url: jdbc:mysql://localhost:3306/seata?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver # Seata配置 seata: enabled: true # 启用Seata application-id: seata-demo # 应用ID tx-service-group: my_test_tx_group # Seata事务组名称 service: vgroup-mapping: my_test_tx_group: default # Seata服务组名称 # 注册中心配置 registry: type: nacos # 注册中心类型 nacos: server-addr: localhost:8848 # 注册中心地址 namespace: public group: SEATA_GROUP file: name: file.conf # 注册中心配置文件名称 path: /seata/config # 注册中心配置文件路径 ``` 3. 配置数据源 在application.yml文件中配置数据源,包括: ``` spring: datasource: url: jdbc:mysql://localhost:3306/seata?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver ``` 4. 配置Seata代理数据源 在Spring Boot启动类中配置Seata代理数据源: ``` @SpringBootApplication @EnableDiscoveryClient @MapperScan("com.example.demo.mapper") @EnableFeignClients(basePackages = "com.example.demo.feign") @EnableTransactionManagement public class SeataDemoApplication { public static void main(String[] args) { SpringApplication.run(SeataDemoApplication.class, args); } @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource dataSource() { return new DruidDataSource(); } @Bean public DataSourceProxy dataSourceProxy(DataSource dataSource) { return new DataSourceProxy(dataSource); } @Bean public GlobalTransactionScanner globalTransactionScanner() { return new GlobalTransactionScanner("seata-demo", "my_test_tx_group"); } } ``` 5. 编写业务代码 在业务代码中使用@GlobalTransactional注解标记需要参与分布式事务的方法,例如: ``` @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Autowired private OrderFeignClient orderFeignClient; @Override @GlobalTransactional public void createOrder(User user) { userMapper.insert(user); orderFeignClient.createOrder(user.getId()); } } ``` 以上就是在Spring Cloud Alibaba中使用Seata进行分布式事务的步骤。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

望天边星宿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值