zoukankan      html  css  js  c++  java
  • SpringCloud分布式事务-Seata

    1 解决问题

    当在Spring Cloud搭建的分布式系统中,如果某个业务涉及到多个服务的事务,无法保证当某一个服务异常时,其他所有业务服务都进行事务的回滚,就会导致业务数据不一致的问题

    2 解决方案

    使用阿里巴巴开源的分布式事务框架Seata,目前支持的注册中心有nacos、eureka、zk、consul、etcd3、sofa等。

    2.1 优点

    1、Seata基于SQL解析实现了事务回滚的自动补偿,无需开发者自己实现,降低了框架对业务的侵入性
    2、事务协调者独立部署成一个微服务,同样降低了对业务的侵入性

    2.2 缺点

    1、Seata的日志回滚表,有一个字段用来存储事务修改数据前后的数据镜像,为了存储内容很大的镜像选择了longblob类型,所以回滚表的插入性能不是很好,即使数据镜像很小

    2.3 Seata支持的分布式事务模式

    1、AT模式:在传统的二阶段提交协议上进行优化,普通的二阶段提交,执行过程中所有节点时同步阻塞的,导致速度很慢,AT模式进行了优化:在第一阶段,节点直接提交本地事务,并记录undo log,然后提交结果到Seata全局的事务管理器;在第二阶段,如果其他服务异常,全局事务管理器异步发送通过undo log记录发送回滚请求到各节点,进行事务的补偿回滚,完毕后删除undo log记录。此模式的前提是本地数据源必须是jdbc连接的支持本地事务的数据库
    2、TCC模式:此模式可以自定义准备、提交、回滚的策略,相对于AT模式,可以支持任何数据源,在第一个准备阶段会将数据加锁,能保证隔离性,需要自己去开发各个阶段的处理逻辑,对业务的侵入性很大,适合不支持事务的数据库,比如impala
    3、Saga模式:本质是二阶段提交的实现版本之一,在第一阶段直接提交本地事务,释放锁,保证高性能,代价是不保证事务的隔离性,适合业务流程长的事务,第二阶段出现异常执行补偿逻辑
    4、XA模式:XA模式需要数据源支持XA协议

    3 搭建流程

    3.1 技术选型

    使用eureka+feign+mybatis,Seata使用AT模式

    3.2 Seata服务端搭建

    1、https://github.com/seata/seata/releases
    下载最新发布版本的压缩包,并解压

    2、配置config目录下的registry.conf
    修改三个地方
    registry.type:指定注册中心,这里指定为eureka
    registry.eureka.serviceUrl:eureka注册地址
    registry.eureka.application:Seata注册到eureka的服务名

    registry {
      # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
      type = "eureka"
      loadBalance = "RandomLoadBalance"
      loadBalanceVirtualNodes = 10
    
      nacos {
        application = "seata-server"
        serverAddr = "127.0.0.1:8848"
        group = "SEATA_GROUP"
        namespace = ""
        cluster = "default"
        username = ""
        password = ""
      }
      eureka {
        serviceUrl = "http://127.0.0.1:8080/eureka/"
        application = "seata-server"
        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 = "127.0.0.1:8848"
        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"
        apolloAccesskeySecret = ""
      }
      zk {
        serverAddr = "127.0.0.1:2181"
        sessionTimeout = 6000
        connectTimeout = 2000
        username = ""
        password = ""
      }
      etcd3 {
        serverAddr = "http://localhost:2379"
      }
      file {
        name = "file.conf"
      }
    }

    2、配置config目录下的file.conf
    service.vgroupMapping.{事务群组}:Seata注册到eureka的服务名
    (注意这里的事务群组,后面要与客户端配置的事务群组一致)
    Seata注册到eureka的服务名.grouplist
    store.mode:指定事务信息存储方式,这里指定为数据库存储
    db下面的配置指定一些数据源的配置,要注意如果使用mysql5和mysql8的driverClassName是不同的

    数据库需要导入三张表,建表语句:https://github.com/seata/seata/tree/develop/script/server/db

    service {
      #transaction service group mapping
      vgroupMapping.my_test_tx_group = "seata-server"
      #only support when registry.type=file, please don't set multiple addresses
      seata-server.grouplist = "127.0.0.1:8091"
      #degrade, current not support
      enableDegrade = false
      #disable seata
      disableGlobalTransaction = false
    }
    
    ## transaction log store, only used in seata-server
    store {
      ## store mode: file、db、redis
      mode = "db"
    
      ## 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)/HikariDataSource(hikari) etc.
        datasource = "druid"
        ## mysql/oracle/postgresql/h2/oceanbase etc.
        dbType = "mysql"
        driverClassName = "com.mysql.cj.jdbc.Driver"
        url = "jdbc:mysql://127.0.0.1:3306/seata?serverTimezone=UTC"
        user = "root"
        password = "root"
        minConn = 5
        maxConn = 100
        globalTable = "global_table"
        branchTable = "branch_table"
        lockTable = "lock_table"
        queryLimit = 100
        maxWait = 5000
      }
    
      ## redis store property
      redis {
        host = "127.0.0.1"
        port = "6379"
        password = ""
        database = "0"
        minConn = 1
        maxConn = 10
        maxTotal = 100
        queryLimit = 100
      }
    
    }

    3、如果需要覆盖Seata服务的eureka配置,可以手动增加eureka-client.properties文件进行配置

    4、前往bin目录,启动Seata服务端,Linux下使用seata-server.sh启动,Windows下使用seata-server.bat启动

    3.3客户端服务搭建

    客户端搭建有两种方式,一种是直接将服务器的配置文件放到客户端项目中,另一种是使用Springboot starter的方式引入。starter的方式更便于配置的统一管理,但是目前有一些starter属性无效,还是要加入一点文件配置,比如disableGlobalTransaction

    3.3.1 引入配置文件方式

    1、客户端为标准的Springboot项目,请自行配置好注册eureka,feign调用,mybatis等配置

    2、引入Seata依赖

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-seata</artifactId>
        <version>2.2.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>1.4.0</version>
    </dependency>

    3、配置bootstrap.yml

    这里指定事务群组,与上方的服务器配置保持一致

    spring:
      cloud:
        alibaba:
          seata:
            tx-service-group: my_test_tx_group

    4、配置数据源

    每个业务数据库需要创建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;

    注意使用Seata的数据源代理DataSourceProxy

    @Configuration
    public class DataSourceConfiguration {
    
        @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();
        }
    
    }

    5、将服务端的配置文件file.conf和registry.conf直接放到客户端新项目的resource目录下

    6、启动客户端项目,查看日志是否register success

     7、在业务的调用函数上加上@GlobalTransactional注解,即可实现分布式事务

    3.3.2 Springboot starter方式

    1、客户端为标准的Springboot项目,请自行配置好注册eureka,feign调用,mybatis等配置

    2、引入Seata依赖

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-seata</artifactId>
        <version>2.2.0.RELEASE</version>
        <exclusions>
            <exclusion>
                <groupId>io.seata</groupId>
                <artifactId>seata-spring-boot-starter</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>io.seata</groupId>
        <artifactId>seata-spring-boot-starter</artifactId>
        <version>1.0.0</version>
    </dependency>

    3、配置bootstrap.yml

    seata:
      enabled: true # 开启seata
      registry: # 指定注册中心
        type: eureka
        eureka:
          service-url: ${eureka.client.service-url.defaultZone}
          application: seata-server
          weight: 1
      service:
        vgroup-mapping: seata-server # seata服务端的服务名
      tx-service-group: my_test_tx_group # 事务群组,与服务端配置一致

    4、配置数据源

    每个业务数据库需要创建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;

    由于starter默认是开启自动数据源代理的,所有不需要额外配置数据源代理

    5、由于部分starter配置不生效,需要将是否禁止全局事务的属性通过配置file.conf文件引入,将此文件放入resource目录下即可

    service {
      disableGlobalTransaction = false
    }

    6、启动客户端项目,查看日志是否register success

    7、在业务的调用函数上加上@GlobalTransactional注解,即可实现分布式事务

  • 相关阅读:
    PAT 甲级 1057 Stack(树状数组解法)
    LeetCode 815 公交路线
    201771010123汪慧和《面向对象程序设计JAVA》第六周实验总结
    汪慧和201771010123《面向对象程序设计JAVA》第四周实验总结
    汪慧和201771010123《面向对象程序设计(Java)》第三周学习总结
    201771010123汪慧和《面向对象程序设计Java》第二周学习总结
    汪慧和201771010123
    201771010119穷吉1
    学习进度条201771010119穷吉
    穷吉201771010119*
  • 原文地址:https://www.cnblogs.com/orange911/p/14102265.html
Copyright © 2011-2022 走看看