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

学习视频

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

项目地址

https://gitee.com/zqcliudaliuda/cloud2021


一、分布式问题

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

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

【SpringCloud Alibaba】Seta安装、处理分布式事务_第1张图片

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

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

二、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下管辖的全部分支事务完成提交或回滚请求。

【SpringCloud Alibaba】Seta安装、处理分布式事务_第2张图片

2.2 注解

只需要使用一个@GlobalTransactional注解在业务方法上:
【SpringCloud Alibaba】Seta安装、处理分布式事务_第3张图片

三、安装

3.1 下载

下载:https://github.com/seata/seata/releases
【SpringCloud Alibaba】Seta安装、处理分布式事务_第4张图片

下载完成后解压。

3.2 file.conf配置

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

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

自定义事务组名称
【SpringCloud Alibaba】Seta安装、处理分布式事务_第5张图片
持久化方法改成数据库
在这里插入图片描述
配置数据库
【SpringCloud Alibaba】Seta安装、处理分布式事务_第6张图片

3.3 数据库

创建数据库
【SpringCloud Alibaba】Seta安装、处理分布式事务_第7张图片
使用seata-server-0.9.0\seata\conf\db_store.sql创建数据库表格。

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

3.4 registry.conf配置

修改注册中心为nacos,且配置注册地址。
【SpringCloud Alibaba】Seta安装、处理分布式事务_第8张图片

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.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-seataartifactId>
        <exclusions>
            <exclusion>
                <groupId>seata-allgroupId>
                <artifactId>io.seataartifactId>
            exclusion>
        exclusions>
    dependency>
    <dependency>
        <groupId>io.seatagroupId>
        <artifactId>seata-allartifactId>
        <version>0.9.0version>
    dependency>
    
    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
    dependency>
    
    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    dependency>
    
    <dependency>
        <groupId>com.alibaba.cspgroupId>
        <artifactId>sentinel-datasource-nacosartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-openfeignartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-actuatorartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-devtoolsartifactId>
        <scope>runtimescope>
        <optional>trueoptional>
    dependency>
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <optional>trueoptional>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <scope>testscope>
    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秒,看看效果
【SpringCloud Alibaba】Seta安装、处理分布式事务_第9张图片

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

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

超时异常,添加@GlobalTransactional

在order业务入口添加@GlobalTransactional注解,当产生异常时进行回滚,保证数据的正确。
【SpringCloud Alibaba】Seta安装、处理分布式事务_第10张图片

你可能感兴趣的:(#,SpringCloud,Java,spring,cloud,java)