zoukankan      html  css  js  c++  java
  • Java分布式事务Seata安装和使用,SpringCloud Seata分布式事务安装配置和使用

    Java分布式事务Seata安装和使用,SpringCloud Seata分布式事务安装配置和使用

    ================================

    ©Copyright 蕃薯耀 2021-05-10

    https://www.cnblogs.com/fanshuyao/

    一、Seata 是什么?
    官方文档:

    https://seata.io/zh-cn/docs/overview/what-is-seata.html

    Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。


    AT 模式基于 支持本地 ACID 事务 的 关系型数据库:
    一阶段 prepare 行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录。
    二阶段 commit 行为:马上成功结束,自动 异步批量清理回滚日志。
    二阶段 rollback 行为:通过回滚日志,自动 生成补偿操作,完成数据回滚。

    TCC 模式,不依赖于底层数据资源的事务支持:
    一阶段 prepare 行为:调用 自定义 的 prepare 逻辑。
    二阶段 commit 行为:调用 自定义 的 commit 逻辑。
    二阶段 rollback 行为:调用 自定义 的 rollback 逻辑。
    所谓 TCC 模式,是指支持把 自定义 的分支事务纳入到全局事务的管理中。

    Seata组成:
    TC (Transaction Coordinator) - 事务协调者
    维护全局和分支事务的状态,驱动全局事务提交或回滚。

    TM (Transaction Manager) - 事务管理器
    定义全局事务的范围:开始全局事务、提交或回滚全局事务。

    RM (Resource Manager) - 资源管理器
    管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。


    官方部署文档:http://seata.io/zh-cn/docs/ops/deploy-guide-beginner.html


    二、Seata Server端安装和配置


    基于seata-server-1.4.2.zip实现,同时也要下载源码包seata-1.4.2.zip(DB模式下要执行SQL脚本以及Nacos作为配置中心,Seata相关的配置需要注册到Nacos)

    下载地址:

    https://github.com/seata/seata/releases

    Server端存储模式(store.mode)现有file、db、redis三种(后续将引入raft,mongodb),file模式无需改动,直接启动即可,下面专门讲下db启动步骤。
    注:
    file模式为单机模式,全局事务会话信息内存中读写并持久化本地文件root.data,性能较高;
    db模式为高可用模式,全局事务会话信息通过db共享,相应性能差些;
    redis模式Seata-Server 1.3及以上版本支持,性能较高,存在事务信息丢失风险,请提前配置合适当前场景的redis持久化配置.


    1、解压:seata-server-1.4.2.zip

    2、进入seata-server目录,修改配置文件

    目录是:

    seata-server-1.4.2conf

    需要修改的配置文件:
    registry.conf
    file.conf

    registry.conf指定注册模式,如 # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa,默认:type = "file",指向file.conf文件
    file.conf:有模式:file、db、redis,用于存储事务的日记。

    registry.conf当前指定使用nacos作为注册中心和配置中心,需要修改registry项和config项,同时修改nacos的连接信息。
    建议增加namespace的配置,将Seata相应的配置放在一起,不然后面配置中心的东西很多,有整整9页

    有命名空间的配置,即修改namespace:

    registry {
      type = "nacos"
    
      nacos {
        application = "seata-server"
        serverAddr = "127.0.0.1:8848"
        group = "SEATA_GROUP"
        namespace = "fb31c4c2-29ac-456c-b9d6-36301baceae4"
        cluster = "default"
        username = ""
        password = ""
      }
    }
    
    config {
      type = "nacos"
    
      nacos {
        serverAddr = "127.0.0.1:8848"
        namespace = "fb31c4c2-29ac-456c-b9d6-36301baceae4"
        group = "SEATA_GROUP"
        username = ""
        password = ""
        dataId = "seataServer.properties"
      }
    }

    file.conf修改内容如下:(需要预先创建一个seata的数据库,用于事务的日志记录)

    ## transaction log store, only used in seata-server
    store {
      mode = "db"
      publicKey = ""
      db {
        datasource = "druid"
        dbType = "mysql"
        driverClassName = "com.mysql.jdbc.Driver"
        url = "jdbc:mysql://127.0.0.1:3307/seata?rewriteBatchedStatements=true"
        user = "root"
        password = "root"
        minConn = 5
        maxConn = 100
        globalTable = "global_table"
        branchTable = "branch_table"
        lockTable = "lock_table"
        queryLimit = 100
        maxWait = 5000
      }
    }

    3、执行seata server端数据库脚本

    Server端:server端数据库脚本 (包含 lock_table、branch_table 与 global_table) 及各个容器配置,脚本需要在源码包找(seata-1.4.2.zip)
    执行脚本(仅使用DB模式才需要执行脚本)
    脚本所在的目录(seata-1.4.2为源码seata-1.4.2.zip):

    seata-1.4.2scriptserverdb

    总共有3个文件(只需要执行自己对应的数据脚本即可):
    mysql.sql
    oracle.sql
    postgresql.sql


    下面为mysql.sql

    -- -------------------------------- The script used when storeMode is 'db' --------------------------------
    -- the table to store GlobalSession data
    CREATE TABLE IF NOT EXISTS `global_table`
    (
        `xid`                       VARCHAR(128) NOT NULL,
        `transaction_id`            BIGINT,
        `status`                    TINYINT      NOT NULL,
        `application_id`            VARCHAR(32),
        `transaction_service_group` VARCHAR(32),
        `transaction_name`          VARCHAR(128),
        `timeout`                   INT,
        `begin_time`                BIGINT,
        `application_data`          VARCHAR(2000),
        `gmt_create`                DATETIME,
        `gmt_modified`              DATETIME,
        PRIMARY KEY (`xid`),
        KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),
        KEY `idx_transaction_id` (`transaction_id`)
    ) ENGINE = InnoDB
      DEFAULT CHARSET = utf8;
    
    -- the table to store BranchSession data
    CREATE TABLE IF NOT EXISTS `branch_table`
    (
        `branch_id`         BIGINT       NOT NULL,
        `xid`               VARCHAR(128) NOT NULL,
        `transaction_id`    BIGINT,
        `resource_group_id` VARCHAR(32),
        `resource_id`       VARCHAR(256),
        `branch_type`       VARCHAR(8),
        `status`            TINYINT,
        `client_id`         VARCHAR(64),
        `application_data`  VARCHAR(2000),
        `gmt_create`        DATETIME(6),
        `gmt_modified`      DATETIME(6),
        PRIMARY KEY (`branch_id`),
        KEY `idx_xid` (`xid`)
    ) ENGINE = InnoDB
      DEFAULT CHARSET = utf8;
    
    -- the table to store lock data
    CREATE TABLE IF NOT EXISTS `lock_table`
    (
        `row_key`        VARCHAR(128) NOT NULL,
        `xid`            VARCHAR(128),
        `transaction_id` BIGINT,
        `branch_id`      BIGINT       NOT NULL,
        `resource_id`    VARCHAR(256),
        `table_name`     VARCHAR(32),
        `pk`             VARCHAR(36),
        `gmt_create`     DATETIME,
        `gmt_modified`   DATETIME,
        PRIMARY KEY (`row_key`),
        KEY `idx_branch_id` (`branch_id`)
    ) ENGINE = InnoDB
      DEFAULT CHARSET = utf8;

    4、启动Seata
    注:启动Seata,记得先启动Nacos

    Windows启动:
    进入目录:seata-server-1.4.2in,双击seata-server.bat直接启动

    Linux命令启动: 
    seata-server.sh -h 127.0.0.1 -p 8091 -m db -n 1 -e test

    源码启动: 执行Server.java的main方法

    参数说明:

    -h: 注册到注册中心的ip
    -p: Server rpc 监听端口
    -m: 全局事务会话信息存储模式,file、db、redis,优先读取启动参数 (Seata-Server 1.3及以上版本支持redis)
    -n: Server node,多个Server时,需区分各自节点,用于生成不同区间的transactionId,以免冲突
    -e: 多环境配置参考 http://seata.io/en-us/docs/ops/multi-configuration-isolation.html

    启动成功后:
    16:52:56.108 INFO --- [ main] io.seata.config.FileConfiguration : The file name of the operation is file.conf
    16:52:56.108 INFO --- [ main] io.seata.config.FileConfiguration : The configuration file used is D:softseata-server-1.4.2conffile.conf
    16:52:57.904 INFO --- [ main] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} inited
    16:52:58.450 INFO --- [ main] i.s.core.rpc.netty.NettyServerBootstrap : Server started, listen port: 8091


    5、导入Seata相应的配置项到Nacos的配置中心(非注册中心)
    这点很容易漏。
    config-center 官网说明地址是:

    https://github.com/seata/seata/tree/develop/script/config-center

    该文件是:config.txt

    注意:在seata-server-1.4.2是没有config.txt文件的,该文件在源码包中:seata-1.4.2.zip,解压后的目录是:

    seata-1.4.2scriptconfig-centerconfig.txt

    (可以将seata-1.4.2scriptconfig-center复制一份到seata-server-1.4.2config-center)

    当前使用的是Nacos作为配置中心(config-center),所以进入到nacos目录:

    seata-1.4.2scriptconfig-center
    acos 

    有两个脚本文件:
    nacos-config.py
    nacos-config.sh

    第一个是phython脚本,第二个是Linux脚本
    那Windows脚本怎么执行呢?安装Git-2.26.2-64-bit.exe,使用Git Bash执行。(省略具体步骤)

    执行前,需要修改config.txt文件:

    my_test_tx_group是默认有的,没有修改。增加分布式事务分组nacos_service_tx_group、nacos_consumer_tx_group。注意:名称长度不能超过VARCHAR(32)。事务分组允许只配置一个,也可以配置成多个,用于切换,此处额外增加2个,具体配置如下:

    service.vgroupMapping.my_test_tx_group=default
    service.vgroupMapping.nacos_service_tx_group=default
    service.vgroupMapping.nacos_consumer_tx_group=default

    全局事务表global_table的transaction_service_group长度为32(VARCHAR(32)),超出会报错。例如:SPRING-CLOUD-NACOS-CONSUMER_tx_group长度为36,就会导致报错。

    service.vgroupMapping.SPRING-CLOUD-NACOS-CONSUMER_tx_group=default:命名超出数据库的长度会报错。

    Caused by: io.seata.core.exception.TmTransactionException: TransactionException[begin global request failed. xid=null, msg=Data truncation: Data too long for column 'transaction_service_group' at row 1]
    at io.seata.tm.DefaultTransactionManager.begin(DefaultTransactionManager.java:55)
    at io.seata.tm.api.DefaultGlobalTransaction.begin(DefaultGlobalTransaction.java:105)
    at io.seata.tm.api.TransactionalTemplate.beginTransaction(TransactionalTemplate.java:215)

    什么是事务分组?见:

    https://seata.io/zh-cn/docs/user/txgroup/transaction-group.html


    修改数据存储模式为db模式,修改数据连接和账号密码

    store.mode=db
    store.db.url=jdbc:mysql://127.0.0.1:3307/seata?useUnicode=true&rewriteBatchedStatements=true
    store.db.user=root
    store.db.password=root


    config.txt修改完成后,通过Git Bash将相应的配置导致到Nacos配置中心,命令如下(路径必须是/,不能是):

    sh D:/0soft/seata-server-1.4.2/config-center/nacos/nacos-config.sh -h 127.0.0.1 -p 8848 -g SEATA_GROUP

    建议增加namespace的配置(-t参数,命名空间的id是在Nacos创建生成的),将Seata相应的配置放在一起,不然后面配置中心的东西很多,有整整9页,而且属性很多,很有可能和其它属性出现重复冲突

    sh D:/0soft/seata-server-1.4.2/config-center/nacos/nacos-config.sh -h 127.0.0.1 -p 8848 -g SEATA_GROUP -t fb31c4c2-29ac-456c-b9d6-36301baceae4

    如果Nacos设置和账号和密码,也可以设置

    sh D:/0soft/seata-server-1.4.2/config-center/nacos/nacos-config.sh -h 127.0.0.1 -p 8848 -g SEATA_GROUP -t fb31c4c2-29ac-456c-b9d6-36301baceae4 -u username -w password

    参数说明:

    -h:主机,默认值为localhost。
    -p:端口,默认值为8848。
    -g:配置分组,默认值为“ SEATA_GROUP”。
    -t:租户信息,对应于Nacos的名称空间ID字段,默认值为空。
    -u:用户名,权限控制在的nacos 1.2.0+,默认值为空。
    -w:密码,权限控制在的nacos 1.2.0+,默认值为空。


    config.txt文件脚本执行后,成功91,失败4个,但失败的不影响Seata运行。

    =========================================================================
     Complete initialization parameters,  total-count:91 ,  failure-count:4
    =========================================================================
     init nacos config fail.

    失败的4个是:

    Set store.publicKey= failure
    Set store.redis.sentinel.masterName= failure
    Set store.redis.sentinel.sentinelHosts= failure
    Set store.redis.password= failure


    不导入config.txt文件,项目加入Seata依赖和配置启动后,会出现下面的错误:can not get cluster name in registry config,详细如下:

    SPRING-CLOUD-NACOS-SERVICE服务提供者:

    2021-05-10 09:27:18.719 ERROR 13544 --- [eoutChecker_1_1] i.s.c.r.netty.NettyClientChannelManager : can not get cluster name in registry config 'service.vgroupMapping.SPRING-CLOUD-NACOS-SERVICE_tx_group', please make sure registry config correct
    2021-05-10 09:27:18.753 ERROR 13544 --- [eoutChecker_2_1] i.s.c.r.netty.NettyClientChannelManager : can not get cluster name in registry config 'service.vgroupMapping.SPRING-CLOUD-NACOS-SERVICE_tx_group', please make sure registry config correct

    SPRING-CLOUD-NACOS-CONSUMER服务消费者:

    2021-05-10 09:27:33.786 ERROR [SPRING-CLOUD-NACOS-CONSUMER,,,] 14916 --- [eoutChecker_1_1] i.s.c.r.netty.NettyClientChannelManager : can not get cluster name in registry config 'service.vgroupMapping.SPRING-CLOUD-NACOS-CONSUMER_tx_group', please make sure registry config correct
    2021-05-10 09:27:33.820 ERROR [SPRING-CLOUD-NACOS-CONSUMER,,,] 14916 --- [eoutChecker_2_1] i.s.c.r.netty.NettyClientChannelManager : can not get cluster name in registry config 'service.vgroupMapping.SPRING-CLOUD-NACOS-CONSUMER_tx_group', please make sure registry config correct


    如果没有什么问题,项目启动后的信息则是register TM success,register success,:

    2021-05-10 10:12:20.315 INFO 2120 --- [eoutChecker_1_1] i.s.c.r.netty.NettyClientChannelManager : will connect to 192.168.170.1:8091
    2021-05-10 10:12:20.319 INFO 2120 --- [eoutChecker_1_1] i.s.core.rpc.netty.NettyPoolableFactory : NettyPool create channel to transactionRole:TMROLE,address:192.168.170.1:8091,msg:< RegisterTMRequest{applicationId='SPRING-CLOUD-NACOS-SERVICE', transactionServiceGroup='nacos_service_tx_group'} >
    2021-05-10 10:12:20.338 INFO 2120 --- [eoutChecker_1_1] i.s.c.rpc.netty.TmNettyRemotingClient : register TM success. client version:1.4.2, server version:1.4.2,channel:[id: 0x0e398621, L:/192.168.170.1:55953 - R:/192.168.170.1:8091]
    2021-05-10 10:12:20.338 INFO 2120 --- [eoutChecker_1_1] i.s.core.rpc.netty.NettyPoolableFactory : register success, cost 7 ms, version:1.4.2,role:TMROLE,channel:[id: 0x0e398621, L:/192.168.170.1:55953 - R:/192.168.170.1:8091]
    
    2021-05-10 10:12:35.467 INFO [SPRING-CLOUD-NACOS-CONSUMER,,,] 13144 --- [eoutChecker_1_1] i.s.c.r.netty.NettyClientChannelManager : will connect to 192.168.170.1:8091 2021-05-10 10:12:35.468 INFO [SPRING-CLOUD-NACOS-CONSUMER,,,] 13144 --- [eoutChecker_1_1] i.s.core.rpc.netty.NettyPoolableFactory : NettyPool create channel to transactionRole:TMROLE,address:192.168.170.1:8091,msg:< RegisterTMRequest{applicationId='SPRING-CLOUD-NACOS-CONSUMER', transactionServiceGroup='nacos_consumer_tx_group'} > 2021-05-10 10:12:35.474 INFO [SPRING-CLOUD-NACOS-CONSUMER,,,] 13144 --- [eoutChecker_1_1] i.s.c.rpc.netty.TmNettyRemotingClient : register TM success. client version:1.4.2, server version:1.4.2,channel:[id: 0xe9bfdcf1, L:/192.168.170.1:55954 - R:/192.168.170.1:8091] 2021-05-10 10:12:35.474 INFO [SPRING-CLOUD-NACOS-CONSUMER,,,] 13144 --- [eoutChecker_1_1] i.s.core.rpc.netty.NettyPoolableFactory : register success, cost 4 ms, version:1.4.2,role:TMROLE,channel:[id: 0xe9bfdcf1, L:/192.168.170.1:55954 - R:/192.168.170.1:8091]


    Seata事务回滚:Rollback global transaction successfully,

    10:13:07.415 INFO --- [rverHandlerThread_1_3_500] i.s.s.coordinator.DefaultCoordinator : Begin new global transaction applicationId: SPRING-CLOUD-NACOS-CONSUMER,transactionServiceGroup: nacos_consumer_tx_group, transactionName: saveClazzAndStudentError(),timeout:60000,xid:192.168.170.1:8091:4936076990459990017
    10:13:14.398 INFO --- [ batchLoggerPrint_1_1] i.s.c.r.p.server.BatchLogHandler : xid=192.168.170.1:8091:4936076990459990017,extraData=null,clientIp:192.168.170.1,vgroup:nacos_consumer_tx_group
    10:13:14.873 INFO --- [rverHandlerThread_1_4_500] io.seata.server.coordinator.DefaultCore : Rollback global transaction successfully, xid = 192.168.170.1:8091:4936076990459990017.


    三、Seata Client 端安装和配置

    1、项目说明
    客户端有2个项目:
    springCloud-8802-nacos-provider:服务提供者
    springCloud-8805-nacos-web:服务消费者

    springCloud-8805-nacos-web会去调用springCloud-8802-nacos-provider,用于测试分布式事务。
    springCloud-8802-nacos-provider连接数据库:seata-biz-1
    springCloud-8805-nacos-web连接数据库:seata-biz-2


    2、Seata Client 端的两个数据库分别执行回滚日志的Sql脚本:undo_log。只需要在客户端的数据库添加表(注意:每个客户端的数据库都要增加,此处为:seata-biz-1和seata-biz-2),Seata服务端的数据是不需要的

    脚本在源码包,目录是:

    seata-1.4.2scriptclientatdb

    mysql.sql脚本如下:

    -- for AT mode you must to init this sql for you business database. the seata server not need it.
    CREATE TABLE IF NOT EXISTS `undo_log`
    (
        `branch_id`     BIGINT       NOT NULL COMMENT 'branch transaction id',
        `xid`           VARCHAR(128) NOT NULL COMMENT 'global transaction id',
        `context`       VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
        `rollback_info` LONGBLOB     NOT NULL COMMENT 'rollback info',
        `log_status`    INT(11)      NOT NULL COMMENT '0:normal status,1:defense status',
        `log_created`   DATETIME(6)  NOT NULL COMMENT 'create datetime',
        `log_modified`  DATETIME(6)  NOT NULL COMMENT 'modify datetime',
        UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
    ) ENGINE = InnoDB
      AUTO_INCREMENT = 1
      DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';

    3、项目添加seata依赖

    Seata依赖说明(建议单选)
    依赖seata-all
    依赖seata-spring-boot-starter,支持yml、properties配置(.conf可删除),内部已依赖seata-all
    依赖spring-cloud-alibaba-seata,内部集成了seata,并实现了xid传递

    以spring-cloud-alibaba-seata为例:

    <properties>
        <seata.version>1.4.2</seata.version>
    </properties>
    
    <dependency>
        <groupId>io.seata</groupId>
        <artifactId>seata-spring-boot-starter</artifactId>
        <version>${seata.version}</version>
    </dependency>
    
    <!-- <version>2.2.5.RELEASE</version> -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
        <exclusions>
        <exclusion>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
        </exclusion>
        </exclusions>
    </dependency>

    4、application.properties增加Seata的配置

    springCloud-8802-nacos-provider项目:

    配置:seata.tx-service-group,和Git 执行脚本生成的Nacos配置的事务分组要保持一致。

    配置:seata.config.nacos.namespace,服务注册的命名空间。

    #更多配置项见:https://seata.io/zh-cn/docs/user/configurations.html
    #是否开启spring-boot自动装配。truefalse,(SSBS)专有配置,默认true
    #seata.enabled=true
    #是否开启数据源自动代理。    truefalse,seata-spring-boot-starter(SSBS)专有配置,SSBS默认会开启数据源自动代理,可通过该配置项关闭.
    #seata.enableAutoDataSourceProxy=true
    #是否使用JDK代理作为数据源自动代理的实现方式。truefalse,(SSBS)专有配置,默认false,采用CGLIB作为数据源自动代理的实
    #seata.useJdkProxy=false
    #一阶段全局提交结果上报TC重试次数,默认1次,建议大于1
    seata.client.tm.commitRetryCount=3
    #一阶段全局回滚结果上报TC重试次数。    默认1次,建议大于1
    seata.client.tm.rollbackRetryCount=3
    #undo序列化方式    默认jackson
    #seata.client.undo.logSerialization
    #自定义undo表名    默认undo_log
    #seata.client.undo.logTable
    
    #类文件:io.seata.spring.boot.autoconfigure.properties.SeataProperties
    seata.application-id=${spring.application.name}
    seata.tx-service-group=nacos_service_tx_group
    
    seata.config.type=nacos
    seata.config.nacos.namespace=fb31c4c2-29ac-456c-b9d6-36301baceae4
    seata.config.nacos.server-addr=127.0.0.1:8848
    seata.config.nacos.group=SEATA_GROUP
    #seata.config.nacos.username=
    #seata.config.nacos.password=
    
    seata.registry.type=nacos
    seata.registry.nacos.application=seata-server
    seata.registry.nacos.server-addr=127.0.0.1:8848
    seata.registry.nacos.group=SEATA_GROUP
    seata.registry.nacos.namespace=fb31c4c2-29ac-456c-b9d6-36301baceae4
    #seata.registry.nacos.username=
    #seata.registry.nacos.password=

    springCloud-8805-nacos-web项目:

    注意配置项:seata.tx-service-group=nacos_consumer_tx_group

    #更多配置项见:https://seata.io/zh-cn/docs/user/configurations.html
    #是否开启spring-boot自动装配。truefalse,(SSBS)专有配置,默认true
    #seata.enabled=true
    #是否开启数据源自动代理。    truefalse,seata-spring-boot-starter(SSBS)专有配置,SSBS默认会开启数据源自动代理,可通过该配置项关闭.
    #seata.enableAutoDataSourceProxy=true
    #是否使用JDK代理作为数据源自动代理的实现方式。truefalse,(SSBS)专有配置,默认false,采用CGLIB作为数据源自动代理的实
    #seata.useJdkProxy=false
    #一阶段全局提交结果上报TC重试次数,默认1次,建议大于1
    seata.client.tm.commitRetryCount=3
    #一阶段全局回滚结果上报TC重试次数。    默认1次,建议大于1
    seata.client.tm.rollbackRetryCount=3
    #undo序列化方式    默认jackson
    #seata.client.undo.logSerialization
    #自定义undo表名    默认undo_log
    #seata.client.undo.logTable
    
    #io.seata.spring.boot.autoconfigure.properties.SeataProperties
    seata.application-id=${spring.application.name}
    seata.tx-service-group=nacos_consumer_tx_group
    
    seata.config.type=nacos
    seata.config.nacos.namespace=fb31c4c2-29ac-456c-b9d6-36301baceae4
    seata.config.nacos.server-addr=127.0.0.1:8848
    seata.config.nacos.group=SEATA_GROUP
    #seata.config.nacos.username=
    #seata.config.nacos.password=
    
    seata.registry.type=nacos
    seata.registry.nacos.application=seata-server
    seata.registry.nacos.server-addr=127.0.0.1:8848
    seata.registry.nacos.group=SEATA_GROUP
    seata.registry.nacos.namespace=fb31c4c2-29ac-456c-b9d6-36301baceae4
    #seata.registry.nacos.username=
    #seata.registry.nacos.password=

    springCloud-8802-nacos-provider项目和springCloud-8805-nacos-web项目的Seata配置的区别是:seata.tx-service-group不同,即事务分组不同。

    5、springCloud-8805-nacos-web项目的服务请求(Service)增加@GlobalTransactional全局事务。

    使用seata-spring-boot-starter,Seata默认是自动配置,自动代理数据源的,所以不需要在启动类加什么注解。只需要在方法加上@GlobalTransactional(rollbackFor = Throwable.class),而且不能加@Transactional(这个注解会导致Seata表的undo_log无数据,但Seata事务能正常提交和回滚)

    @Transactional
        @Override
        public Clazz save() {
            Clazz clazz = new Clazz();
            clazz.setClazzName("班级:" + RandomUtil.randomString(5));
            
            this.baseMapper.insert(clazz);
            
            return clazz;
        }
        
        
        @Transactional
        @Override
        public void saveClassAndStudent() {
            Clazz clazz = this.save();
            Result resultStudent=  restTemplate.getForObject("http://" + myNacosServiceName + "/student/save", Result.class);
            
            log.info("clazz=" + JsonUtil.toJson(clazz));
            log.info("resultStudent=" + JsonUtil.toJson(resultStudent));
        }
        
        
        /**
         * 这里作为事务发起端,加@GlobalTransactional,启动全局事务
         * 这里不能加@Transactional,不然受到本地事务管理,导致Seata产生的日志受到本地事务管理,在服务提供者端打断点看不到Seata表中有数据。
         */
        @GlobalTransactional(rollbackFor = Throwable.class)
        @Override
        public void saveClazzAndStudentError() {
            //本地事务的方法
            Clazz clazz = this.save();
            
            //调用服务提供者的方法
            Result resultStudent=  restTemplate.getForObject("http://" + myNacosServiceName + "/student/saveError", Result.class);
            
            log.info("clazz=" + JsonUtil.toJson(clazz));
            log.info("resultStudent=" + JsonUtil.toJson(resultStudent));
        }

    springCloud-8802-nacos-provider项目是不需要增加@GlobalTransactional全局事务,在事务发起端配置@GlobalTransactional即可。

    //@Transactional//(使用这个注解会导致Seata表的undo_log无数据)
    @Override
    public Student save() {
        Student s = new Student();
        s.setStuName("学生:" + RandomUtil.randomString(5));
        s.setStuNo(UidUtil.getUid());
        
        this.baseMapper.insert(s);
        
        return s;
    }
    
    
    //@Transactional//(使用这个注解会导致Seata表的undo_log无数据)
    @Override
    public Student saveError() {
        Student s = new Student();
        s.setStuName("学生:" + RandomUtil.randomString(5));
        s.setStuNo(UidUtil.getUid());
        
        this.baseMapper.insert(s);
        
        
        int a = 0; 
        System.out.println(3/a);
        
        return s;
    }

    然后进行Seata分布式事务测试,包括成功和不成功(回滚)。


    Seata分布式事务的配置和使用,到此已经完结。

    后面的是扩展,有兴趣可继续往下看。

    四、Seata分布式事务其它项说明


    1、数据源代理(不支持自动和手动配置并存,不支持XA数据源自动代理)

    0.9.0版本开始seata支持自动代理数据源

    1.1.0: seata-all取消属性配置,改由注解@EnableAutoDataSourceProxy开启,并可选择jdk proxy或者cglib proxy
    1.0.0: client.support.spring.datasource.autoproxy=true
    0.9.0: support.spring.datasource.autoproxy=true


    手动配置可参考下方的例子

        @Primary
        @Bean("dataSource")
        public DataSourceProxy dataSource(DataSource druidDataSource) {
            //AT 代理 二选一
            return new DataSourceProxy(druidDataSource);
            //XA 代理
            return new DataSourceProxyXA(druidDataSource)
        }

    2、初始化GlobalTransactionScanner


    手动方式:

           @Bean
           public GlobalTransactionScanner globalTransactionScanner() {
               String applicationName = this.applicationContext.getEnvironment().getProperty("spring.application.name");
               String txServiceGroup = this.seataProperties.getTxServiceGroup();
               if (StringUtils.isEmpty(txServiceGroup)) {
                   txServiceGroup = applicationName + "-fescar-service-group";
                   this.seataProperties.setTxServiceGroup(txServiceGroup);
               }
       
               return new GlobalTransactionScanner(applicationName, txServiceGroup);
           }

    自动方式:

    引入seata-spring-boot-starter、spring-cloud-starter-alibaba-seata等jar

    3、实现xid跨服务传递
    手动 参考源码integration文件夹下的各种rpc实现 module
    自动 springCloud用户可以引入spring-cloud-starter-alibaba-seata,内部已经实现xid传递


    五、Seata事务分组
    官网说明:

    https://seata.io/zh-cn/docs/user/txgroup/transaction-group-and-ha.html

    事务分组是什么?
    事务分组是seata的资源逻辑,类似于服务实例。在file.conf中的my_test_tx_group就是一个事务分组。

    通过事务分组如何找到后端集群?
    首先程序中配置了事务分组(GlobalTransactionScanner 构造方法的txServiceGroup参数)
    程序会通过用户配置的配置中心去寻找service.vgroupMapping .[事务分组配置项],取得配置项的值就是TC集群的名称
    拿到集群名称程序通过一定的前后缀+集群名称去构造服务名,各配置中心的服务名实现不同
    拿到服务名去相应的注册中心去拉取相应服务名的服务列表,获得后端真实的TC服务列表

    为什么这么设计,不直接取服务名?
    这里多了一层获取事务分组到映射集群的配置。这样设计后,事务分组可以作为资源的逻辑隔离单位,出现某集群故障时可以快速failover,只切换对应分组,可以把故障缩减到服务级别,但前提也是你有足够server集群。


    最佳实践1:TC的异地多机房容灾


    假定TC集群部署在两个机房:guangzhou机房(主)和shanghai机房(备)各两个实例
    一整套微服务架构项目:projectA
    projectA内有微服务:serviceA、serviceB、serviceC 和 serviceD
    其中,projectA所有微服务的事务分组tx-transaction-group设置为:projectA,projectA正常情况下使用guangzhou的TC集群(主)

    那么正常情况下,client端的配置如下所示:
    seata.tx-service-group=projectA
    seata.service.vgroup-mapping.projectA=Guangzhou

    假如此时guangzhou集群分组整个down掉,或者因为网络原因projectA暂时无法与Guangzhou机房通讯,那么我们将配置中心中的Guangzhou集群分组改为Shanghai,如下:
    seata.service.vgroup-mapping.projectA=Shanghai

    并推送到各个微服务,便完成了对整个projectA项目的TC集群动态切换。

    最佳实践2:单一环境多应用接入


    假设现在开发环境(或预发/生产)中存在一整套seata集群
    seata集群要服务于不同的微服务架构项目projectA、projectB、projectC
    projectA、projectB、projectC之间相对独立
    我们将seata集群中的六个实例两两分组,使其分别服务于projectA、projectB与projectC,那么此时seata-server端的配置如下(以nacos注册中心为例)

    最佳实践3:client的精细化控制


    假定现在存在seata集群,Guangzhou机房实例运行在性能较高的机器上,Shanghai集群运行在性能较差的机器上
    现存微服务架构项目projectA、projectA中有微服务ServiceA、ServiceB、ServiceC与ServiceD
    其中ServiceD的流量较小,其余微服务流量较大
    那么此时,我们可以将ServiceD微服务引流到Shanghai集群中去,将高性能的服务器让给其余流量较大的微服务(反之亦然,若存在某一个微服务流量特别大,我们也可以单独为此微服务开辟一个更高性能的集群,并将该client的virtual group指向该集群,其最终目的都是保证在流量洪峰时服务的可用)

    六、Seata配置中心


    什么是配置中心?配置中心可以说是一个"大衣柜",内部放置着各种配置文件,你可以通过自己所需进行获取配置加载到对应的客户端.比如Seata Client端(TM,RM),Seata Server(TC),会去读取全局事务开关,事务会话存储模式等信息.
    Seata的配置中心与Spring cloud的配置中心区别是?在广义上来说,并无区别,只不过Spring cloud的配置中心仅是作用于它们自身的组件,而Seata的配置中心也是一样是作用于Seata自身.(注:Spring cloud的配置中心与Seata无关)
    Seata支持哪些配置中心?
    consul
    apollo
    etcd
    zookeeper
    redis
    file (读本地文件)

    七、Seata注册中心


    什么是注册中心?注册中心可以说是微服务架构中的”通讯录“,它记录了服务和服务地址的映射关系。在分布式架构中,服务会注册到这里,当服务需要调用其它服务时,就到这里找到服务的地址,进行调用.比如Seata Client端(TM,RM),发现Seata Server(TC)集群的地址,彼此通信.
    Seata的注册中心与Dubbo,Spring cloud的注册中心区别是?在广义上来说,并无区别,只不过Dubbo与Spring cloud的注册中心仅是作用于它们自身的组件,而Seata的注册中心也是一样是作用于Seata自身.(注:Dubbo与Spring cloud的注册中心与Seata无关)
    Seata支持哪些注册中心?
    eureka
    consul
    apollo
    etcd
    zookeeper
    sofa
    redis
    file (直连)

    八、Seata微服务框架支持


    1、Seata事务上下文

    Seata 的事务上下文由 RootContext 来管理。
    应用开启一个全局事务后,RootContext 会自动绑定该事务的 XID,事务结束(提交或回滚完成),RootContext 会自动解绑 XID。

    // 绑定 XID
    RootContext.bind(xid);
    
    // 解绑 XID
    String xid = RootContext.unbind();
    应用可以通过 RootContext 的 API 接口来获取当前运行时的全局事务 XID。
    
    // 获取 XID
    String xid = RootContext.getXID();
    应用是否运行在一个全局事务的上下文中,就是通过 RootContext 是否绑定 XID 来判定的。
    
    public static boolean inGlobalTransaction() {
    return CONTEXT_HOLDER.get(KEY_XID) != null;
    }


    2、Seata事务传播
    Seata 全局事务的传播机制就是指事务上下文的传播,根本上,就是 XID 的应用运行时的传播方式。

    1. 服务内部的事务传播

    默认的,RootContext 的实现是基于 ThreadLocal 的,即 XID 绑定在当前线程上下文中。

    所以服务内部的 XID 传播通常是天然的通过同一个线程的调用链路串连起来的。默认不做任何处理,事务的上下文就是传播下去的。

    如果希望挂起事务上下文,则需要通过 RootContext 提供的 API 来实现:

    // 挂起(暂停)
    String xid = RootContext.unbind();
    
    // TODO: 运行在全局事务外的业务逻辑
    
    // 恢复全局事务上下文
    RootContext.bind(xid);

    2. 跨服务调用的事务传播

    通过上述基本原理,我们可以很容易理解:

    跨服务调用场景下的事务传播,本质上就是要把 XID 通过服务调用传递到服务提供方,并绑定到 RootContext 中去。

    只要能做到这点,理论上 Seata 可以支持任意的微服务框架

    九、Seata各种模式比较


    1、Seata AT 模式:https://seata.io/zh-cn/docs/dev/mode/at-mode.html

    前提
    基于支持本地 ACID 事务的关系型数据库。
    Java 应用,通过 JDBC 访问数据库。

    整体机制
    两阶段提交协议的演变:

    一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。

    二阶段:

    提交异步化,非常快速地完成。
    回滚通过一阶段的回滚日志进行反向补偿。


    2、Seata TCC 模式(https://seata.io/zh-cn/docs/dev/mode/tcc-mode.html)

    回顾总览中的描述:一个分布式的全局事务,整体是 两阶段提交 的模型。全局事务是由若干分支事务组成的,分支事务要满足 两阶段提交 的模型要求,即需要每个分支事务都具备自己的:

    一阶段 prepare 行为
    二阶段 commit 或 rollback 行为

    根据两阶段行为模式的不同,我们将分支事务划分为 Automatic (Branch) Transaction Mode 和 TCC (Branch) Transaction Mode.

    AT 模式(参考链接 TBD)基于 支持本地 ACID 事务 的 关系型数据库:
    一阶段 prepare 行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录。
    二阶段 commit 行为:马上成功结束,自动 异步批量清理回滚日志。
    二阶段 rollback 行为:通过回滚日志,自动 生成补偿操作,完成数据回滚。

    相应的,TCC 模式,不依赖于底层数据资源的事务支持:
    一阶段 prepare 行为:调用 自定义 的 prepare 逻辑。
    二阶段 commit 行为:调用 自定义 的 commit 逻辑。
    二阶段 rollback 行为:调用 自定义 的 rollback 逻辑。
    所谓 TCC 模式,是指支持把 自定义 的分支事务纳入到全局事务的管理中。


    3、SEATA Saga 模式(https://seata.io/zh-cn/docs/user/saga.html)
    概述
    Saga模式是SEATA提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。

    适用场景:
    业务流程长、业务流程多
    参与者包含其它公司或遗留系统服务,无法提供 TCC 模式要求的三个接口

    优势:
    一阶段提交本地事务,无锁,高性能
    事件驱动架构,参与者可异步执行,高吞吐
    补偿服务易于实现

    缺点:
    不保证隔离性(应对方案见后面文档)


    4、Seata XA 模式(https://seata.io/zh-cn/docs/dev/mode/xa-mode.html)
    前提
    支持XA 事务的数据库。
    Java 应用,通过 JDBC 访问数据库。

    整体机制
    在 Seata 定义的分布式事务框架内,利用事务资源(数据库、消息服务等)对 XA 协议的支持,以 XA 协议的机制来管理分支事务的一种 事务模式。

    十、Seata1.4.2问题踩坑

    1、Seata branch_table无事务数据、Seata  lock_table 无事务数据、Seata  undo_log无数据、Seata  undo_log无回滚日志、seata1.4 @Transactional冲突 

    在测试过程中,发现Seata  branch_table、lock_table 、undo_log无数据,只有global_table有数据,但Seata 事务能正常提交、回滚。

    但为什么没数据呢?

    最开始是@GlobalTransactional和@Transactional两个注解一起使用,虽然Seata事务能正常提交和回滚,但后面发现Seata  undo_log无数据,只有global_table有一条数据,其它表也没有数据

        @GlobalTransactional(rollbackFor = Throwable.class)
        @Transactional
        @Override
        public void saveClazzAndStudentError() {
            Clazz clazz = this.save();
            Result resultStudent=  restTemplate.getForObject("http://" + myNacosServiceName + "/student/saveError", Result.class);
            
            log.info("clazz=" + JsonUtil.toJson(clazz));
            log.info("resultStudent=" + JsonUtil.toJson(resultStudent));
        }

     原因竟然是:seata的@GlobalTransactional和@Transactional冲突 

    后面所有项目的所有方法都改成只使用@GlobalTransactional时,所有表都有数据了,而且Seata事务也能正常提交和回滚。暂时不知道是什么原因导致的。

    @GlobalTransactional(rollbackFor = Throwable.class)

    十一、注意事项

     官网注意事项:

    http://seata.io/zh-cn/docs/overview/faq.html

    1、使用 AT 模式需要的注意事项有哪些 ?

    1. 必须使用代理数据源,有 3 种形式可以代理数据源:
    • 依赖 seata-spring-boot-starter 时,自动代理数据源,无需额外处理。
    • 依赖 seata-all 时,使用 @EnableAutoDataSourceProxy (since 1.1.0) 注解,注解参数可选择 jdk 代理或者 cglib 代理。
    • 依赖 seata-all 时,也可以手动使用 DatasourceProxy 来包装 DataSource。
    1. 配置 GlobalTransactionScanner,使用 seata-all 时需要手动配置,使用 seata-spring-boot-starter 时无需额外处理。
    2. 业务表中必须包含单列主键,若存在复合主键,请参考问题 13 。
    3. 每个业务库中必须包含 undo_log 表,若与分库分表组件联用,分库不分表。
    4. 跨微服务链路的事务需要对相应 RPC 框架支持,目前 seata-all 中已经支持:Apache Dubbo、Alibaba Dubbo、sofa-RPC、Motan、gRpc、httpClient,对于 Spring Cloud 的支持,请大家引用 spring-cloud-alibaba-seata。其他自研框架、异步模型、消息消费事务模型请结合 API 自行支持。
    5. 目前AT模式支持的数据库有:MySQL、Oracle、PostgreSQL和 TiDB。
    6. 使用注解开启分布式事务时,若默认服务 provider 端加入 consumer 端的事务,provider 可不标注注解。但是,provider 同样需要相应的依赖和配置,仅可省略注解。
    7. 使用注解开启分布式事务时,若要求事务回滚,必须将异常抛出到事务的发起方,被事务发起方的 @GlobalTransactional 注解感知到。provide 直接抛出异常 或 定义错误码由 consumer 判断再抛出异常。

    2、AT 模式和 Spring @Transactional 注解连用时需要注意什么 ?

    @Transactional 可与 DataSourceTransactionManager 和 JTATransactionManager 连用分别表示本地事务和XA分布式事务,大家常用的是与本地事务结合。当与本地事务结合时,@Transactional和@GlobalTransaction连用,@Transactional 只能位于标注在@GlobalTransaction的同一方法层次或者位于@GlobalTransaction 标注方法的内层。这里分布式事务的概念要大于本地事务,若将 @Transactional 标注在外层会导致分布式事务空提交,当@Transactional 对应的 connection 提交时会报全局事务正在提交或者全局事务的xid不存在。

    3、怎么使用Seata框架,来保证事务的隔离性?

    A: 因seata一阶段本地事务已提交,为防止其他事务脏读脏写需要加强隔离。

      1. 脏读 select语句加for update,代理方法增加@GlobalLock+@Transactional或@GlobalTransaction
      2. 脏写 必须使用@GlobalTransaction
        注:如果你查询的业务的接口没有GlobalTransactional 包裹,也就是这个方法上压根没有分布式事务的需求,这时你可以在方法上标注@GlobalLock+@Transactional 注解,并且在查询语句上加 for update。 如果你查询的接口在事务链路上外层有GlobalTransactional注解,那么你查询的语句只要加for update就行。设计这个注解的原因是在没有这个注解之前,需要查询分布式事务读已提交的数据,但业务本身不需要分布式事务。 若使用GlobalTransactional注解就会增加一些没用的额外的rpc开销比如begin 返回xid,提交事务等。GlobalLock简化了rpc过程,使其做到更高的性能。

    4、脏数据回滚失败如何处理?

    A:

    1. 脏数据需手动处理,根据日志提示修正数据或者将对应undo删除(可自定义实现FailureHandler做邮件通知或其他)
    2. 关闭回滚时undo镜像校验,不推荐该方案。

    5、是否可以不使用conf类型配置文件,直接将配置写入application.properties?

    A: 目前seata-all是需要使用conf类型配置文件,后续会支持properties和yml类型文件。当前可以在项目中依赖seata-spring-boot-starter,然后将配置项写入到application .properties 这样可以不使用conf类型文件。

    6、使用mybatis-plus 动态数据源组件后undolog无法删除 ?

    A:

    dynamic-datasource-spring-boot-starter 组件内部开启seata后会自动使用DataSourceProxy来包装DataSource,所以需要以下方式来保持兼容

    1.如果你引入的是seata-all,请不要使用@EnableAutoDataSourceProxy注解.

    2.如果你引入的是seata-spring-boot-starter 请关闭自动代理

    seata:
      enable-auto-data-source-proxy: false



    (时间宝贵,分享不易,捐赠回馈,^_^)

    ================================

    ©Copyright 蕃薯耀 2021-05-10

    https://www.cnblogs.com/fanshuyao/

    今天越懒,明天要做的事越多。
  • 相关阅读:
    Appium移动自动化测试-----(十三)appium API 之其他操作
    Appium移动自动化测试-----(十二)appium API 之 TouchAction 操作
    Appium移动自动化测试-----(十一)appium API 之键盘操作
    Appium移动自动化测试-----(十)appium API 之上下文操作
    Appium移动自动化测试-----(九) appium API 之应用操作
    Appium移动自动化测试-----(八)定位控件
    Linux ls
    Pointer's NULL And 0
    JS 实现"可读"字符串转换成"二进制的01"字符串
    Qt QT_BEGIN_NAMESPACE
  • 原文地址:https://www.cnblogs.com/fanshuyao/p/14750450.html
Copyright © 2011-2022 走看看