• 分布式事务处理----seata


    项目集成背景springCloud-eureka-feign-mybatis-seata

    想更清楚的理解全局事务框架SeaTa,可以参考资料 https://www.jianshu.com/p/044e95223a17,我认为介绍的很详细,下面来搭建集成并测试结果 参考:https://segmentfault.com/a/1190000020639849

    一、安装部署Seata Server

    Seata目前在github托管开源源代码,源码地址:https://github.com/seata/seata

    1.下载Seata Server

    很多博客中文章中都给出了下载地址:Seata Server最新版本下载     https://github.com/seata/seata/releases/tag/v1.2.0  两个安装包,.zip支持Windows;  .tar.gz支持Linux(这里下载的是1.2.0版本)

     博主的百度云盘有有存两个1.2.0版本的安装包,可以直接下载:

    seata-server-1.2.0.zip

    链接:https://pan.baidu.com/s/11UudlHOorckhXgNwv1GsuA
    提取码:g38r

    seata-server-1.2.0.tar.gz

    链接:https://pan.baidu.com/s/1gh5ogjph7wP6NfCFw2Vqvw
    提取码:t4ik

     Linux系统下我们通过命令  tar -xvf 来解压tar.gz压缩文件:

    tar -xvf seata-server-1.2.0.tar.gz

    解压完成后我们得到了几个文件夹。

    • bin

      存放各个系统的seata server启动脚本

    • conf

      存在seata server启动时所需要的配置信息、数据库模式下所需要的建表语句

    • lib

      运行seata server所需要的依赖包列表

    2.配置Seata Server

    seata server所有的配置都在conf文件夹内,该文件夹内有两个文件我们必须要详细介绍下。

    seata server默认使用file(文件方式)进行存储事务日志事务运行信息,我们可以通过-m db脚本参数的形式来指定,目前仅支持filedb这两种方式。

    • file.conf

      该文件用于配置存储方式透传事务信息的NIO等信息,默认对应registry.conf文件内的file方式配置。

    • registry.conf

      seata server核心配置文件,可以通过该文件配置服务注册方式配置读取方式

      注册方式目前支持file 、nacos 、eureka、redis、zk、consul、etcd3、sofa等方式,默认为file,对应读取file.co读取配置信息的方式支持file、nacos 、apollo、zk、consul、etcd3等方式,默认为file,对应读取file.conf## transaction log store, only used in seata-server

    file.conf

    ## transaction log store, only used in seata-server
    store {
      ## store mode: file、db
      mode = "file"
    
      ## file store property
      file {
        ## store location dir
        dir = "sessionStore"
        # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
        maxBranchSessionSize = 16384
        # globe session size , if exceeded throws exceptions
        maxGlobalSessionSize = 512
        # file buffer size , if exceeded allocate new buffer
        fileWriteBufferCacheSize = 16384
        # when recover batch read size
        sessionReloadReadSize = 100
        # async, sync
        flushDiskMode = async
      }
    
      ## database store property
      db {
        ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
        datasource = "druid"
        ## mysql/oracle/postgresql/h2/oceanbase etc.
        dbType = "mysql"
        driverClassName = "com.mysql.jdbc.Driver"
        url = "jdbc:mysql://127.0.0.1:3306/seata"
        user = "mysql"
        password = "mysql"
        minConn = 5
        maxConn = 30
        globalTable = "global_table"
        branchTable = "branch_table"
        lockTable = "lock_table"
        queryLimit = 100
        maxWait = 5000
      }
    }
    View Code

    registry.conf

    registry {
      # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
      type = "file"
    
      nacos {
        application = "seata-server"
        serverAddr = "localhost"
        namespace = ""
        cluster = "default"
        username = ""
        password = ""
      }
      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"
      }
      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 = "file"
    
      nacos {
        serverAddr = "localhost"
        namespace = ""
        group = "SEATA_GROUP"
        username = ""
        password = ""
      }
      consul {
        serverAddr = "127.0.0.1:8500"
      }
      apollo {
        appId = "seata-server"
        apolloMeta = "http://192.168.1.204:8801"
        namespace = "application"
      }
      zk {
        serverAddr = "127.0.0.1:2181"
        sessionTimeout = 6000
        connectTimeout = 2000
        username = ""
        password = ""
      }
      etcd3 {
        serverAddr = "http://localhost:2379"
      }
      file {
        name = "file.conf"
      }
    }
    View Code

    根据自己的环境进行配置:

    registry.conf   我选择eureka做注册中心,其他不变如下图

     file.conf  配置存储类型db,配置数据库,如下图

     3.启动Seata Server

    启动seata server的脚本位于bin文件内,Linux/Mac环境使用seata-server.sh脚本启动,Windows环境使用seata-server.bat脚本启动。

    Linux/Mac启动方式示例如下所示:

    nohup sh seata-server.sh -p 8091 -h 127.0.0.1 -m file &> seata.log &

    通过nohup命令让seata server在系统后台运行。

    脚本参数:

    • -p

      指定启动seata server的端口号。

    • -h

      指定seata server所绑定的主机,这里配置要注意指定的主机IP要与业务服务内的配置文件保持一致,如:-h 192.168.1.10,业务服务配置文件内应该配置192.168.1.10,即使在同一台主机上也要保持一致。

    • -m

      事务日志、事务执行信息存储的方式,目前支持file(文件方式)、db(数据库方式,建表语句请查看config/db_store.sqlconfig/db_undo_log.sql

    事务日志、事务执行信息存储的方式选择db 需要创建数据库seata   sql如下

    -- the table to store GlobalSession data
    drop table if exists `global_table`;
    create table `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`)
    );
    
    -- the table to store BranchSession data
    drop table if exists `branch_table`;
    create table `branch_table` (
      `branch_id` bigint not null,
      `xid` varchar(128) not null,
      `transaction_id` bigint ,
      `resource_group_id` varchar(32),
      `resource_id` varchar(256) ,
      `lock_key` varchar(128) ,
      `branch_type` varchar(8) ,
      `status` tinyint,
      `client_id` varchar(64),
      `application_data` varchar(2000),
      `gmt_create` datetime,
      `gmt_modified` datetime,
      primary key (`branch_id`),
      key `idx_xid` (`xid`)
    );
    
    -- the table to store lock data
    drop table if exists `lock_table`;
    create table `lock_table` (
      `row_key` varchar(128) not null,
      `xid` varchar(96),
      `transaction_id` long ,
      `branch_id` long,
      `resource_id` varchar(256) ,
      `table_name` varchar(32) ,
      `pk` varchar(36) ,
      `gmt_create` datetime ,
      `gmt_modified` datetime,
      primary key(`row_key`)
    );
    View Code

    二、项目集成

    经典案例可以从GitHub上下载开源demo  https://github.com/seata/seata-samples

    为了方便这里将案例工程springcloud-eureka-feign-mybatis-seata放在百度云盘上,可以直接提取

    链接:https://pan.baidu.com/s/1bumMZV8Fd4dpQoFrd4CwiQ
    提取码:dy7l

    依赖的seata  maven包(无法将file.conf和registry.conf两个配置文件中的配置写到  .properties配置文件中)

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-seata</artifactId>
        <version>2.1.0.RELEASE</version>
        <exclusions>
            <exclusion>
                <artifactId>seata-all</artifactId>
                <groupId>io.seata</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>io.seata</groupId>
        <artifactId>seata-all</artifactId>
        <version>${seata.version}</version>
    </dependency>

    为了方便配置(将file.conf和registry.conf两个配置文件中的配置写到  .properties配置文件中)需要依赖:

    <dependency>
              <groupId>com.alibaba</groupId>
              <artifactId>druid-spring-boot-starter</artifactId>
              <version>1.1.10</version>
          </dependency>
    <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-seata</artifactId> <version>2.2.0.RELEASE</version> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>1.2.0</version>
    </dependency>

    配置文件: application.properties

    seata.enabled=true
    seata.application-id=user-svr
    seata.tx-service-group=fsp_tx_user_svr_group
    
    # seata.client
    # 事务群组(可以每个应用独立取名,也可以使用相同的名字)
    seata.client.rm.report-success-enable=true
    # 自动刷新缓存中的表结构(默认false)
    seata.client.rm.table-meta-check-enable=false
    # 一阶段结果上报TC重试次数(默认5)
    seata.client.rm.report-retry-count=5
    # 异步提交缓存队列长度(默认10000)
    seata.client.rm.async-commit-buffer-limit=10000
    # 校验或占用全局锁重试间隔(默认10ms)
    seata.client.rm.lock.retry-interval=10
    # 校验或占用全局锁重试次数(默认30)
    seata.client.rm.lock.retry-times=30
    # 分支事务与其它全局回滚事务冲突时锁策略(优先释放本地锁让回滚成功)
    seata.client.rm.lock.retry-policy-branch-rollback-on-conflict=true
    # 一阶段全局提交结果上报TC重试次数(默认1次,建议大于1)
    seata.client.tm.commit-retry-count=3
    # 一阶段全局回滚结果上报TC重试次数(默认1次,建议大于1)
    seata.client.tm.rollback-retry-count=3
    # 二阶段回滚镜像校验(默认true开启)
    seata.client.undo.data-validation=true
    # undo序列化方式(默认jackson)
    seata.client.undo.log-serialization=jackson
    # 自定义undo表名(默认undo_log)
    seata.client.undo.log-table=undo_log
    # 日志异常输出概率(默认100)
    seata.client.log.exceptionRate=100
    
    # seata.service
    # TC 集群(必须与seata-server保持一致)
    seata.service.vgroup-mapping.fsp_tx_user_svr_group=default
    # 降级开关
    seata.service.enable-degrade=false
    # 禁用全局事务(默认false)
    seata.service.disable-global-transaction=false
    seata.service.grouplist.default=192.168.1.33:8091
    
    # seata.transport
    seata.transport.type=TCP
    seata.transport.server=NIO
    seata.transport.heartbeat=true
    seata.transport.serialization=seata
    seata.transport.compressor=none
    seata.transport.shutdown.wait=3
    seata.transport.thread-factory.boss-thread-prefix=NettyBoss
    seata.transport.thread-factory.worker-thread-prefix=NettyServerNIOWorker
    seata.transport.thread-factory.server-executor-thread-prefix=NettyServerBizHandler
    seata.transport.thread-factory.share-boss-worker=false
    seata.transport.thread-factory.client-selector-thread-prefix=NettyClientSelector
    seata.transport.thread-factory.client-selector-thread-size=1
    seata.transport.thread-factory.client-worker-thread-prefix=NettyClientWorkerThread
    # 客户端事务消息请求是否批量合并发送(默认true)
    seata.transport.enable-client-batch-send-request=true
    
    #seata.registry
    seata.registry.type=file
    seata.config.type=file
    seata.config.file.name=file.conf

    在业务相关的数据库中添加 undo_log 表,用于保存需要回滚的数据

    CREATE TABLE `undo_log`
    (
        `id`            BIGINT(20)   NOT NULL AUTO_INCREMENT,
        `branch_id`     BIGINT(20)   NOT NULL,
        `xid`           VARCHAR(100) NOT NULL,
        `context`       VARCHAR(128) NOT NULL,
        `rollback_info` LONGBLOB     NOT NULL,
        `log_status`    INT(11)      NOT NULL,
        `log_created`   DATETIME     NOT NULL,
        `log_modified`  DATETIME     NOT NULL,
        `ext`           VARCHAR(100) DEFAULT NULL,
        PRIMARY KEY (`id`),
        UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
    ) ENGINE = InnoDB
      AUTO_INCREMENT = 1
      DEFAULT CHARSET = utf8

    四个服务:注册中心:eureka-server ;订单服务:order-server ;仓库服务:storage-server ;账户服务:account-server ;演示的业务是增加一个订单,账户减金额,仓库减库存,生成订单

     为了调同通这个demo废了老大劲了

    1)修改配置文件application.yml以仓库服务为例:为了方面,这里我创建了一个数据库inst1,三个工程都有sql文件,在数据库中建表,切记还要创建undo_log 表

    2)修改registry.conf文件,主要修改使用注册中心的类型为:Eureka,如下

     3)修改file.conf 如下

     注:这里需要重点说一个坑,仓库中下载下来的工程中vgroupMapping的配置存在问题,找了很久一直报错

      no available service 'null' found, please make sure registry config correct

    忽视掉的一个配置yml中的  spring.cloud.alibaba.seata.tx-service-group= fsp_tx_storage_server_group  必须要和 file.conf 中 vgroupMapping 的 key 值为 fsp_tx_storage_server_group :

    spring:
        application:
            name: storage-server
        cloud:
            alibaba:
                seata:
                    tx-service-group: fsp_tx_storage_server_group

    否则就会报错!!!!

    三、测试调试

    为了方便验证结果,可以在订单服务中做如下修改操作,然后启动所有工程进行调试,看数据库中的数据变化,以及异常是否回滚

     

    四、几个坑点 

    1、代理数据源的配置

    import com.alibaba.druid.pool.DruidDataSource;
    import io.seata.rm.datasource.DataSourceProxy;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    
    import javax.sql.DataSource;
    
    /**
     * Created by IntelliJ IDEA
     * 这是一个神奇的Class
     *
     * @author zhz
     * @date 2020/5/21 20:41
     */
    @Configuration
    public class DataSourceProxyConfig {
    
        @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();
        }
    
    }

    2、启动类配置

    SpringBootApplication注解需要添加 DataSourceAutoConfiguration 让代理数据源起效
    @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})

    3、SpringBoot和springCloud的版本问题引起的

    Spring Cloud                                                     Spring Boot
    Angel版本                                               兼容Spring Boot 1.2.x
    Brixton版本                                             兼容Spring Boot 1.3.x,也兼容Spring Boot 1.4.x
    Camden版本                                           兼容Spring Boot 1.4.x,也兼容Spring Boot 1.5.x
    Dalston版本、Edgware版本                   兼容Spring Boot 1.5.x,不兼容Spring Boot 2.0.x
    Finchley版本                                           兼容Spring Boot 2.0.x,不兼容Spring Boot 1.5.x
    Greenwich版本                                       兼容Spring Boot 2.1.x

    SpringCloud的Finchley、Greenwich两个版本的@FeignClient 的属性value(name)略有不同,Finchley可以保持多个同名的feign,而Greenwich必须只有一个

    4、在实际配置中,项目是SpringBoot+SpringCloud+Feign+Redis+Shiro+Seata,会产生目前不明的bug,博主推测是是Shiro和Seata的兼容问题,导致事务的分支事务无法回滚;

    5、数据库表存在多主键的表,暂时不支持

     https://www.cnblogs.com/victorbu/p/12738556.html

    这只是一个简单的springCloud-eureka-feign-mybatis-seata的demo测试,如何集成到自己的项目中还是要自己摸索清楚,至少调通了这个demo就迈出了一大步

  • 相关阅读:
    如何实现浏览器内多个标签页之间的通信?
    vue组件库的基本开发步骤(源代码)
    vue组件库的基本开发步骤
    Websocket原理
    TCP和UDP的区别
    一句话概括 tcp三次握手
    简单说一下你对http和https的理解
    .Ajax(async异步与sync同步)
    get和post请求方式的区别
    面试易忽略状态码
  • 原文地址:https://www.cnblogs.com/wdzhz/p/12925136.html
Copyright © 2020-2023  润新知