• Seata-AT模式


    1、AT模式介绍

            AT 模式是一种无侵入的分布式事务解决方案。在 AT 模式下,用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。

    2、AT模式原理 

      在介绍AT 模式的时候它是无侵入的分布式事务解决方案, 那么如何做到对业务的无侵入的呢?

      一阶段:

            在一阶段,Seata 会拦截“业务 SQL”,首先解析 SQL 语义,找到“业务 SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”,然后执行“业务 SQL”更新业务数据,在业务数据更新之后,再将其保存成“after image”,最后生成行锁。以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。

      二阶段:

            提交:二阶段如果是提交的话,因为“业务 SQL”在一阶段已经提交支数据库, 所以 Seata 框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。

            回滚:二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的“业务 SQL”,还原业务数据。回滚方式便是用“before image”还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image”,如果两份数据完全⼀致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。

            AT 模式的一阶段、二阶段提交和回滚均由 Seata 框架自动生成,用户只需编写“业务SQL”,便能轻松接入分布式事务,AT 模式是一种对业务无任何侵入的分布式事务解决方案。 

    3、AT模式改造案例

    (1)Seata Server - TC全局事务协调器

            介绍了 seata 事务的三个模块:TC(事务协调器)、TM(事务管理器)和RM(资源管理器),其中 TM 和 RM 是嵌入在业务应用中的,而 TC 则是一个独立服务。

            Seata Server 就是 TC,直接从官方仓库下载启动即可,        

            下载地址:https://github.com/seata/seata/releases

    registry.conf:

            Seata Server 要向注册中心进行注册,这样,其他服务就可以通过注册中心去发现 Seata Server,与 Seata Server 进行通信。

            Seata 支持多款注册中心服务:nacos 、eureka、redis、zk、consul、etcd3、sofa。我们项目中要使用 nacos注册中心,nacos服务的连接地址、注册的服务名,这需要在seata/conf/registry.conf文件中进行配置:

    registry {
      # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
      type = "nacos"

      nacos {
        application = "seata-tc-server"
        serverAddr = "127.0.0.1:8848"
        group = "SEATA_GROUP"
        namespace = ""
        cluster = "default"
        username = "nacos"
        password = "nacos"
      }
      eureka {
        serviceUrl = "http://localhost:8761/eureka"
        application = "default"
        weight = "1"
      }
      redis {
        serverAddr = "localhost:6379"
        db = 0
        password = ""
        cluster = "default"
        timeout = 0
      }
      zk {
        cluster = "default"
        serverAddr = "127.0.0.1:2181"
        sessionTimeout = 6000
        connectTimeout = 2000
        username = ""
        password = ""
      }
      consul {
        cluster = "default"
        serverAddr = "127.0.0.1:8500"
        aclToken = ""
      }
      etcd3 {
        cluster = "default"
        serverAddr = "http://localhost:2379"
      }
      sofa {
        serverAddr = "127.0.0.1:9603"
        application = "default"
        region = "DEFAULT_ZONE"
        datacenter = "DefaultDataCenter"
        cluster = "default"
        group = "SEATA_GROUP"
        addressWaitTime = "3000"
      }
      file {
        name = "file.conf"
      }
    }

    config {
      # file、nacos 、apollo、zk、consul、etcd3
      type = "nacos"

      nacos {
        serverAddr = "127.0.0.1:8848"
        namespace = ""
        group = "SEATA_GROUP"
        username = "nacos"
        password = "nacos"
      }
      consul {
        serverAddr = "127.0.0.1:8500"
        aclToken = ""
      }
      apollo {
        appId = "seata-server"
        ## apolloConfigService will cover apolloMeta
        apolloMeta = "http://192.168.1.204:8801"
        apolloConfigService = "http://192.168.1.204:8080"
        namespace = "application"
        apolloAccesskeySecret = ""
        cluster = "seata"
      }
      zk {
        serverAddr = "127.0.0.1:2181"
        sessionTimeout = 6000
        connectTimeout = 2000
        username = ""
        password = ""
        nodePath = "/seata/seata.properties"
      }
      etcd3 {
        serverAddr = "http://localhost:2379"
      }
      file {
        name = "file.conf"
      }
    }

    向nacos中添加配置信息:

      访问 https://seata.io/zh-cn/docs/user/configurations.html,其里面有针对每个一项配置介绍 

    • 将config.txt文件放入seata目录下面

    • 修改config.txt信息 

            Server端存储的模式(store.mode)现有file,db,redis三种。主要存储全局事务会话信息,分支事务信息, 锁记录表信息,seata-server默认是file模式。file只能支持单机模式, 如果想要高可用模式的话可以切换db或者redis. 为了方便查看全局事务会话信息本次课程采用db数据库模式

    存储模式:

    store.mode=db

    mysql数据库连接信息

    store.db.datasource=druid
    store.db.dbType=mysql
    store.db.driverClassName=com.mysql.jdbc.Driver
    store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true
    store.db.user=root
    store.db.password=root
    store.db.minConn=5
    store.db.maxConn=30
    store.db.globalTable=global_table
    store.db.branchTable=branch_table
    store.db.queryLimit=100
    store.db.lockTable=lock_table
    store.db.maxWait=5000

    注意1: 需要创建seata数据库

    注意2: 需要创建global_table/branch_table/lock_table三张表,seata1.0以上就不自带数据库文件了,要自己去github下载https://github.com/seata/seata/tree/develop/script/server/db 

    1. -- -------------------------------- The script used when storeMode is 'db' --------------------------------
    2. -- the table to store GlobalSession data
    3. CREATE TABLE
    4. IF
    5. NOT EXISTS `global_table` (
    6. `xid` VARCHAR ( 128 ) NOT NULL,
    7. `transaction_id` BIGINT,
    8. `status` TINYINT NOT NULL,
    9. `application_id` VARCHAR ( 32 ),
    10. `transaction_service_group` VARCHAR ( 32 ),
    11. `transaction_name` VARCHAR ( 128 ),
    12. `timeout` INT,
    13. `begin_time` BIGINT,
    14. `application_data` VARCHAR ( 2000 ),
    15. `gmt_create` DATETIME,
    16. `gmt_modified` DATETIME,
    17. PRIMARY KEY ( `xid` ),
    18. KEY `idx_status_gmt_modified` ( `status`, `gmt_modified` ),
    19. KEY `idx_transaction_id` ( `transaction_id` )
    20. ) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;-- the table to store BranchSession data
    21. CREATE TABLE
    22. IF
    23. NOT EXISTS `branch_table` (
    24. `branch_id` BIGINT NOT NULL,
    25. `xid` VARCHAR ( 128 ) NOT NULL,
    26. `transaction_id` BIGINT,
    27. `resource_group_id` VARCHAR ( 32 ),
    28. `resource_id` VARCHAR ( 256 ),
    29. `branch_type` VARCHAR ( 8 ),
    30. `status` TINYINT,
    31. `client_id` VARCHAR ( 64 ),
    32. `application_data` VARCHAR ( 2000 ),
    33. `gmt_create` DATETIME ( 6 ),
    34. `gmt_modified` DATETIME ( 6 ),
    35. PRIMARY KEY ( `branch_id` ),
    36. KEY `idx_xid` ( `xid` )
    37. ) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;-- the table to store lock data
    38. CREATE TABLE
    39. IF
    40. NOT EXISTS `lock_table` (
    41. `row_key` VARCHAR ( 128 ) NOT NULL,
    42. `xid` VARCHAR ( 128 ),
    43. `transaction_id` BIGINT,
    44. `branch_id` BIGINT NOT NULL,
    45. `resource_id` VARCHAR ( 256 ),
    46. `table_name` VARCHAR ( 32 ),
    47. `pk` VARCHAR ( 36 ),
    48. `status` TINYINT NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',
    49. `gmt_create` DATETIME,
    50. `gmt_modified` DATETIME,
    51. PRIMARY KEY ( `row_key` ),
    52. KEY `idx_status` ( `status` ),
    53. KEY `idx_branch_id` ( `branch_id` ),
    54. KEY `idx_xid` ( `xid` )
    55. ) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;
    56. CREATE TABLE IF NOT EXISTS `distributed_lock`
    57. (
    58. `lock_key` CHAR(20) NOT NULL,
    59. `lock_value` VARCHAR(20) NOT NULL,
    60. `expire` BIGINT,
    61. primary key (`lock_key`)
    62. ) ENGINE = InnoDB
    63. DEFAULT CHARSET = utf8mb4;
    64. INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('AsyncCommitting', ' ', 0);
    65. INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryCommitting', ' ', 0);
    66. INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryRollbacking', ' ', 0);
    67. INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('TxTimeoutCheck', ' ', 0);
    • 使用nacos-config.sh 用于向 Nacos 中添加配置:

            下载地址:https://github.com/seata/seata/tree/develop/script/config-center/nacos

    将nacos-config.sh放在seata/conf文件夹中 

    打开git bash here 执行nacos-config.sh,需要提前将nacos启动 

    输入命令 :

    sh nacos-config.sh -h 127.0.0.1

    登录nacos查看配置信息 

    启动seata-server 

    观察nacos服务列表 

    (2) TM/RM端整合Seata

            AT 模式在RM端需要 UNDO_LOG 表,来记录每个RM的事务信息,主要包含数据修改前,后的相关信息,用于回滚处理,所以在所有数据库中分别执行 

    1. -- 注意此处0.3.0+ 增加唯⼀索引 ux_undo_log
    2. CREATE TABLE `undo_log` (
    3. `id` BIGINT ( 20 ) NOT NULL AUTO_INCREMENT,
    4. `branch_id` BIGINT ( 20 ) NOT NULL,
    5. `xid` VARCHAR ( 100 ) NOT NULL,
    6. `context` VARCHAR ( 128 ) NOT NULL,
    7. `rollback_info` LONGBLOB NOT NULL,
    8. `log_status` INT ( 11 ) NOT NULL,
    9. `log_created` datetime NOT NULL,
    10. `log_modified` datetime NOT NULL,
    11. `ext` VARCHAR ( 100 ) DEFAULT NULL,
    12. PRIMARY KEY ( `id` ),
    13. UNIQUE KEY `ux_undo_log` ( `xid`, `branch_id` )
    14. ) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;

    TM/RM端整合seata一共有五个步骤:

    事务分组: 

            RM(事务管理器)端整合Seata与TM(事务管理器)端步骤类似,只不过不需要在方法添加@GlobalTransactional注解,针对我们工程lagou_bussiness是事务的发起者,所以是TM端,其它工程为RM端. 所以我们只需要在lagou_common_db完成前4步骤即可 

    工程中添加Seata依赖

    • lagou_parent添加seata依赖管理,用于seata的版本锁定
      1. <dependencyManagement>
      2. <dependencies>
      3. <dependency>
      4. <groupId>org.springframework.cloudgroupId>
      5. <artifactId>spring-cloud-dependenciesartifactId>
      6. <version>Greenwich.RELEASEversion>
      7. <type>pomtype>
      8. <scope>importscope>
      9. dependency>
      10. <dependency>
      11. <groupId>com.alibaba.cloudgroupId>
      12. <artifactId>spring-cloud-alibaba-dependenciesartifactId>
      13. <version>2.1.0.RELEASEversion>
      14. <type>pomtype>
      15. <scope>importscope>
      16. dependency>
      17. <dependency>
      18. <groupId>mysqlgroupId>
      19. <artifactId>mysql-connector-javaartifactId>
      20. <version>5.1.47version>
      21. dependency>
      22. <dependency>
      23. <groupId>io.seatagroupId>
      24. <artifactId>seata-allartifactId>
      25. <version>1.3.0version>
      26. dependency>
      27. dependencies>
      28. dependencyManagement>
    • 在lagou_common_db工程添加seata依赖
      1. <dependency>
      2. <groupId>com.alibaba.cloudgroupId>
      3. <artifactId>spring-cloud-alibaba-seataartifactId>
      4. <exclusions>
      5. <exclusion>
      6. <groupId>io.seatagroupId>
      7. <artifactId>seata-allartifactId>
      8. exclusion>
      9. exclusions>
      10. dependency>
      11. <dependency>
      12. <groupId>io.seatagroupId>
      13. <artifactId>seata-allartifactId>
      14. dependency>

    在common工程添加registry.conf依赖

    添加公共配置 

    spring.cloud.alibaba.seata.tx-service-group=default_tx_group
    logging.level.io.seata=debug

    在每个模块下引入公共配置文件 

    profiles
            active: seata

    编译数据源代理(公共模块)

    1. package com.lagou.common_db;
    2. import com.alibaba.druid.pool.DruidDataSource;
    3. import io.seata.rm.datasource.DataSourceProxy;
    4. import org.springframework.boot.context.properties.ConfigurationProperties;
    5. import org.springframework.context.annotation.Bean;
    6. import org.springframework.context.annotation.Configuration;
    7. import org.springframework.context.annotation.Primary;
    8. import javax.sql.DataSource;
    9. /**
    10. * 数据源代理
    11. */
    12. @Configuration
    13. public class DataSourceConfiguration {
    14. /**
    15. * 使用druid连接池
    16. *
    17. * @return
    18. */
    19. @Bean
    20. @ConfigurationProperties(prefix = "spring.datasource")
    21. public DataSource druidDataSource() {
    22. return new DruidDataSource();
    23. }
    24. /**
    25. * 设置数据源代理,完成分支事务注册/事务提交与回滚等操作
    26. *
    27. * @param druidDataSource
    28. * @return
    29. */
    30. @Primary // 设置首选数据源对象
    31. @Bean("dataSource")
    32. public DataSourceProxy dataSource(DataSource druidDataSource) {
    33. return new DataSourceProxy(druidDataSource);
    34. }
    35. }

    启动扫描配置类,分别加载每个工程的启动类中

    1. @SpringBootApplication(exclude = DataSourceAutoConfiguration.class,
    2. scanBasePackages = "com.lagou")

    添加注解@GlobalTransactional

    1. /**
    2. * 商品销售
    3. *
    4. * @param goodsId 商品id
    5. * @param num 销售数量
    6. * @param username 用户名
    7. * @param money 金额
    8. */
    9. // @Transactional
    10. @GlobalTransactional(name = "sale", timeoutMills = 100000, rollbackFor = Exception.class)
    11. public void sale(Integer goodsId, Integer num, Double money, String username) {
    12. //创建订单
    13. orderServiceFeign.addOrder(idWorker.nextId(), goodsId, num, money, username);
    14. //增加积分
    15. pointsServiceFeign.increase(username, (int) (money / 10));
    16. //扣减库存
    17. storageServiceFeign.decrease(goodsId, num);
    18. }

                                                                                                                                                        资源

  • 相关阅读:
    Web 前端 之 Vue webpack 环境的搭建及工程创建简单整理
    LuatOS-SOC接口文档(air780E)--iotauth - IoT鉴权库, 用于生成各种云平台的参数
    vue3 快速入门系列 —— vue3 路由
    [漏洞复现] jenkins 远程代码执行 (CVE-2019-100300)
    【Kingbase FlySync】命令模式:部署双轨并行,并实现切换同步
    Python面试高频问题:self到底是什么
    23..【摆脱list链表的束缚、让你爱上链表】
    思泰克在创业板过会:拟募资4亿元,赛富投资、传音控股等为股东
    LSTM基础
    RK3588平台开发系列讲解(Thermal篇)Thermal的设备树配置
  • 原文地址:https://blog.csdn.net/weixin_52851967/article/details/126692349