SpringBoot 整合 SpringCloud Alibab-Seata 详解

文章目录

  • SpringCloud Alibab-Seata简介:
  • 一、SpringBoot 整合 Seata
    • 1、前置要求
      • 1、创建表 undo_log
      • 2、安装事务协调器(seata-server)
    • 2、导入依赖
    • 3、添加Seata配置
      • 方案一:`file.conf` + `registry.conf` (原始配置)
        • `file.conf `示例
        • ` registry.conf `示例
        • application.yml
      • 方案二:seata 配置整合到 `application.yml `文件中
      • 方案三:seata客户端配置整合到配置中心:
      • 读取配置源码分析:
        • `ConfigurationFactory`
        • `ExtConfigurationProvider`接口的实现类:
        • `ConfigurationProvider` 接口的实现类:
    • 4、将 seata 的 `DataSourceProxy` 设置为主数据源,包住原来的 `DataSource` 数据源,否则事务无法回滚
      • 源码分析:
          • `SeataDataSourceAutoConfiguration`
          • `SeataAutoDataSourceProxyCreator`
    • 5、可以启动微服务,并测试分布式事务
  • 二、FAQ
    • 问题1、Seata 启动报错 `can not get cluster name in registry config 'service.vgroupMapping.default_tx_group', please make sure registry config correct`
      • 复现:
      • 解决:
        • 方案1:改为从文件配置的方式获取配置
        • 方案2:“配置中心读取的方式”有个坑:除了添加自定义的`seata.properties`配置文件外, 还需要额外添加 DataId 为`service.vgroupMapping.default_tx_group`,值为`default`。
          • 比如:Nacos配置中心,Nacos 关于Seata客户端的配置,如下图:
          • application.yml (相当于将`registry.conf`配置文件的配置整合到这里 )
          • seata-clien-file-conf.yml(相当于`file.conf`配置文件)
        • 成功解决
      • 解决过程中的坑:
      • 总结:
    • 问题2、SpringBoot 整合完 Seata 后,发现分布式事务并没有起作用?
      • 1、检查 依赖是否引入正确.只导入 `seata-spring-boot-starter`依赖是不够的。
      • 2、如果是“从文件读取配置的方式(`registry.type=file` 和`config.type=file` )”,检查 classes 目录下是否加载了配置文件。
      • 3、@GlobalTransactional 注解是否起效果?
  • 三、Seata AT 模式解决分布式事务的弊端
          • 柔性事务-最大努力通知方案
          • 柔性事务-可靠消息+最终一致性方案(异步确保型)
  • 四、相关链接:

SpringCloud Alibab-Seata简介:

​ Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。

Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

原Fescar,即分布式事务解决方案。

​ 详细介绍,可以阅读 SpringCloud Alibab-Seata 官网

一、SpringBoot 整合 Seata

1、前置要求

整合Seata之前,必须已经做了如下的前置要求:

1、创建表 undo_log

  • SEATA AT 模式需要 每一个微服务对应的数据库表必须先创建表 undo_log(回滚记录表)
-- 注意此处0.3.0+ 增加唯一索引 ux_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;

2、安装事务协调器(seata-server)

下载并安装最新版的事务协调器(seata-server)。

  • 下载 seata-server V1.5.1 ,GitHub下载地址
  • 执行/conf 目录下的启动文件,正常启动。

2、导入依赖

 
<dependency>
    <groupId>com.alibaba.cloudgroupId>
    <artifactId>spring-cloud-starter-alibaba-seataartifactId>
     
    <version>${spring-cloud-alibaba.version}version>
dependency>
 
<dependency>
    <groupId>io.seatagroupId>
    <artifactId>seata-spring-boot-starterartifactId>
    <version>1.5.1version>
dependency>

maven 依赖关系图如下:

maven依赖关系图

3、添加Seata配置

方案一:file.conf + registry.conf (原始配置)

  • seata 配置方式默认是 file,从文件读取,seata也提供了其他的配置方式,比如放到:Nacos、Apollo等配置中心。

    • 以下讲解默认的 file配置方式,如需要使用配置中心的方式,请自行抽取到配置中心:

      1. file.conf 文件添加到 /resources配置路径下,跟 application.yml同级。

      2. registry.conf 文件添加到 /resources配置路径下,跟 application.yml同级。

file.conf 示例

最新的示例配置,请参考官方文档。此示例是拷贝自 v1.5.0 - v1.5.1 seata conf配置文件地址 。

  • 这里重点关注service.vgroupMapping.default_tx_group = "default"配置,default_tx_group这个是v1.5.1版本默认事务组名字,如果想自定义这个事务组名字,可以配置 seata.tx-service-group=default_tx_group自定义。
  • 如果对不上,当客户端启动时会注册不到 seata-server,控制台会报错can not get cluster name in registry config 'service.vgroupMapping.default_tx_group', please make sure registry config correct
transport {
  # tcp udt unix-domain-socket
  type = "TCP"
  #NIO NATIVE
  server = "NIO"
  #enable heartbeat
  heartbeat = true
  # the client batch send request enable
  enableClientBatchSendRequest = true
  #thread factory for netty
  threadFactory {
    bossThreadPrefix = "NettyBoss"
    workerThreadPrefix = "NettyServerNIOWorker"
    serverExecutorThread-prefix = "NettyServerBizHandler"
    shareBossWorker = false
    clientSelectorThreadPrefix = "NettyClientSelector"
    clientSelectorThreadSize = 1
    clientWorkerThreadPrefix = "NettyClientWorkerThread"
    # netty boss thread size,will not be used for UDT
    bossThreadSize = 1
    #auto default pin or 8
    workerThreadSize = "default"
  }
  shutdown {
    # when destroy server, wait seconds
    wait = 3
  }
  serialization = "seata"
  compressor = "none"
}
service {
  #transaction service group mapping
  vgroupMapping.default_tx_group = "default"
  #only support when registry.type=file, please don't set multiple addresses
  default.grouplist = "127.0.0.1:8091"
  #degrade, current not support
  enableDegrade = false
  #disable seata
  disableGlobalTransaction = false
}

client {
  rm {
    asyncCommitBufferLimit = 10000
    lock {
      retryInterval = 10
      retryTimes = 30
      retryPolicyBranchRollbackOnConflict = true
    }
    reportRetryCount = 5
    tableMetaCheckEnable = false
    reportSuccessEnable = false
  }
  tm {
    commitRetryCount = 5
    rollbackRetryCount = 5
  }
  undo {
    dataValidation = true
    logSerialization = "jackson"
    logTable = "undo_log"
  }
  log {
    exceptionRate = 100
  }
}

registry.conf 示例

最新的示例配置,请参考官方文档。此示例是拷贝自 v1.5.0 - v1.5.1 seata conf配置文件地址 。

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  # 指定注册类型,这里使用 file 文件读取配置模式,如果使用`nacos`配置中心,则修改对应`naocs`的配置即可(不要忘记将当前配置文件迁移到配置中心喔)
  type = "file"

  nacos {
	application = "seata-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"
  }
  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"
    dataId = "seataServer.properties"
  }
  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"
  }
}

application.yml

高版本的 Springboot 启动时,控制台可能会报com.alibaba.cloud.seata.rest.SeataRestTemplateAutoConfiguration循环依赖的错。需要配置一下spring.main.allow-circular-references=true,允许循环依赖即可。

spring:
  application:
    name: seata-test
  main:
    # 允许循环依赖,避免启动时"com.alibaba.cloud.seata.rest.SeataRestTemplateAutoConfiguration"循环依赖报错
    allow-circular-references: true
server:
  port: 9000

# seata 事务组默认配置(解决v1.5.1版本配置失效问题)
seata:
  tx-service-group: default_tx_group # 可以自定义事务组名字,"default_tx_group"是默认值,如果改了这里,file.conf配置下的 `server.vgroupMapping.xxxx`也要跟着一起改,否则会注册不到 “seata-server”导致报错。
logging:
  level:
    io.seata: debug

方案二:seata 配置整合到 application.yml 文件中

相当于 file.conf + registry.conf ,整合到 application.yml中。

####################################################################################################
# Seata 配置
seata:
  enabled: true # 默认开启
  application-id: ${spring.application.name}
  tx-service-group: default_tx_group # 事务群组(可以每个应用独立取名,也可以使用相同的名字)
  client:
    rm-report-success-enable: true
    rm-table-meta-check-enable: false # 自动刷新缓存中的表结构(默认false)
    rm-report-retry-count: 5 # 一阶段结果上报TC重试次数(默认5)
    rm-async-commit-buffer-limit: 10000 # 异步提交缓存队列长度(默认10000)
    rm:
      lock:
        lock-retry-internal: 10 # 校验或占用全局锁重试间隔(默认10ms)
        lock-retry-times: 30 # 校验或占用全局锁重试次数(默认30)
        lock-retry-policy-branch-rollback-on-conflict: true # 分支事务与其它全局回滚事务冲突时锁策略(优先释放本地锁让回滚成功)
    tm-commit-retry-count: 3 # 一阶段全局提交结果上报TC重试次数(默认1次,建议大于1)
    tm-rollback-retry-count: 3 # 一阶段全局回滚结果上报TC重试次数(默认1次,建议大于1)
    undo:
      undo-data-validation: true # 二阶段回滚镜像校验(默认true开启)
      undo-log-serialization: jackson # undo序列化方式(默认jackson)
      undo-log-table: undo_log  # 自定义undo表名(默认undo_log)
    log:
      exception-rate: 100 # 日志异常输出概率(默认100)
    support:
      spring:
        datasource-autoproxy: true
  service:
    vgroup-mapping:
      default_tx_group: default # TC 集群(必须与seata-server保持一致)
    grouplist:
      default: 127.0.0.1:8091
    enable-degrade: false # 降级开关
    disable-global-transaction: false # 禁用全局事务(默认false)

  transport:
    type: TCP
    server: NIO
    heartbeat: true
    serialization: seata
    compressor: none
    enable-client-batch-send-request: true # 客户端事务消息请求是否批量合并发送(默认true)
    shutdown:
      wait: 3
    thread-factory:
      boss-thread-prefix: NettyBoss
      worker-thread-prefix: NettyServerNIOWorker
      server-executor-thread-prefix: NettyServerBizHandler
      share-boss-worker: false
      client-selector-thread-prefix: NettyClientSelector
      client-selector-thread-size: 1
      client-worker-thread-prefix: NettyClientWorkerThread

  registry:
    type: file  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  config:
    #    file:
    #      name: file.conf
    type: file  # file、nacos 、apollo、zk、consul、etcd3
####################################################################################################

方案三:seata客户端配置整合到配置中心:

  • 略,以Nacos为例,详情见下方 “FAQ-问题1-方案2”

读取配置源码分析:

ConfigurationFactory

public final class ConfigurationFactory {
    
    private static Configuration buildConfiguration() {
        //1、获取 `config.type`配置类型
        String configTypeName = CURRENT_FILE_INSTANCE.getConfig(
                ConfigurationKeys.FILE_ROOT_CONFIG + ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR
                        + ConfigurationKeys.FILE_ROOT_TYPE);
        //略。。。。
        
        //2、 file 类型(本地file文件方式读取配置)
        if (ConfigType.File == configType) {
            String pathDataId = String.join(ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR,
                    ConfigurationKeys.FILE_ROOT_CONFIG, FILE_TYPE, NAME_KEY);
            String name = CURRENT_FILE_INSTANCE.getConfig(pathDataId);
            configuration = new FileConfiguration(name);
            try {
                //2.1  `ExtConfigurationProvider` 接口,唯一实现类 `SpringBootConfigurationProvider`
                extConfiguration = EnhancedServiceLoader.load(ExtConfigurationProvider.class).provide(configuration);
               //略...
            }
            //略...
        } else {
          
            //3、其他类型读取配置走到这里
            //3.1 `ConfigurationProvider`接口有很多实现类,比如:Nacos、apollo等配置中心读取
            configuration = EnhancedServiceLoader
                    .load(ConfigurationProvider.class, Objects.requireNonNull(configType).name()).provide();
        }
        //略。。。不是重点
        return null == extConfiguration ? configuration : extConfiguration;
    }
}

ExtConfigurationProvider接口的实现类:

ExtConfigurationProvider`接口的实现类

ConfigurationProvider 接口的实现类:

ConfigurationProvider` 接口的实现类

以 Nacos 为例,seata.config.nacos.data-id读取的配置不为空,则添加一个监听器,专门监听该配置的变更。

Nacos读取不到配置时截图

Nacos能读取到配置时截图

4、将 seata 的 DataSourceProxy 设置为主数据源,包住原来的 DataSource 数据源,否则事务无法回滚

  • 引入spring-cloud-starter-alibaba-seata依赖后,io.seata.spring.boot.autoconfigure.SeataDataSourceAutoConfiguration已帮我们自动注入处理了。

源码分析:

SeataDataSourceAutoConfiguration
@ConditionalOnBean({DataSource.class})
@ConditionalOnExpression("${seata.enabled:true} && ${seata.enableAutoDataSourceProxy:true} && ${seata.enable-auto-data-source-proxy:true}")
@AutoConfigureAfter({SeataCoreAutoConfiguration.class})
public class SeataDataSourceAutoConfiguration {
    public SeataDataSourceAutoConfiguration() {
    }

    @Bean({"seataAutoDataSourceProxyCreator"})
    @ConditionalOnMissingBean({SeataAutoDataSourceProxyCreator.class})//如果缺少这个类`SeataAutoDataSourceProxyCreator`,则自动注入。
    public SeataAutoDataSourceProxyCreator seataAutoDataSourceProxyCreator(SeataProperties seataProperties) {
        //【重点】这个类`SeataAutoDataSourceProxyCreator`帮我封装了DataSource处理了。
        //seataProperties.getDataSourceProxyMode():默认使用"AT"模式
        return new SeataAutoDataSourceProxyCreator(seataProperties.isUseJdkProxy(), seataProperties.getExcludesForAutoProxying(), seataProperties.getDataSourceProxyMode());
    }
}
SeataAutoDataSourceProxyCreator
import javax.sql.DataSource;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import io.seata.core.model.BranchType;
import io.seata.rm.datasource.DataSourceProxy;
import io.seata.rm.datasource.SeataDataSourceProxy;
import io.seata.rm.datasource.xa.DataSourceProxyXA;
import org.aopalliance.aop.Advice;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator;
import org.springframework.aop.support.DefaultIntroductionAdvisor;
public class SeataAutoDataSourceProxyCreator extends AbstractAutoProxyCreator {
	//其他代码略。

    SeataDataSourceProxy buildProxy(DataSource origin, String proxyMode) {
      
        if (BranchType.AT.name().equalsIgnoreCase(proxyMode)) {
            //将 DataSource 给seata的 DataSourceProxy 代理
            return new DataSourceProxy(origin);
        }
        if (BranchType.XA.name().equalsIgnoreCase(proxyMode)) {
             //将 DataSource 给seata的 DataSourceProxy 代理
            return new DataSourceProxyXA(origin);
        }
        throw new IllegalArgumentException("Unknown dataSourceProxyMode: " + proxyMode);
    }
}

5、可以启动微服务,并测试分布式事务

1、给分布式大事务的入口标注 @GlobalTransactional ,效果可以简单的理解成跟Spring提供的@Transactional作用雷同。不同的是,seata的@GlobalTransactional注解,默认利用AT模式,通过2PC模型,控制分布式事务。(当然,前提是其他微服务也有引入seata,并重复上面1-2步骤。)

2、被远程调用的其他微服务方法的方法正常使用 @Transactional 注解,代表控制住自己的事务。跟@GlobalTransactional注解不冲突,应该同时使用。

测试项目,以官方的子项目 springcloud-nacos-seata 为例:

  • 当有需要记录的事务时,会在对应数据库的 undo_log 表,新增一条记录。

undo_log 截图

undo_log回滚事务

  • Seata-Server 控制台 全局事务日志打印。可以从图中看到 “AT模式”,自动控制全局事务的提交和回滚。

AT模式的全局事务控制日志截图

二、FAQ

问题1、Seata 启动报错 can not get cluster name in registry config 'service.vgroupMapping.default_tx_group', please make sure registry config correct

今天学习使用的时候发现客户端启动成功后控制台一直报错can not get cluster name in registry config 'service.vgroupMapping.default_tx_group', please make sure registry config correct,问题解决过程记录如下:

服务端 seata-server 版本 : 1.5.1

客户端 seata-spring-boot-starter 版本: 1.5.1

复现:

  1. 下载完最新的 seata-server 1.5.1 ,解压后,修改 application.yml配置文件为nacos 方式,然后执行启动文件(在安装目录 \bin\seata-server.bat), 双击直接启动服务端。

  2. 客户端导入 seata-spring-boot-starter 依赖后启动,控制台疯狂报错。can not get cluster name in registry config 'service.vgroupMapping.default_tx_group', please make sure registry config correct

(如果是部署到Linux系统报的这个问题,也是一样的原因)

解决:

后来debug查看了源码,才发现我在 application.yml配置文件中设置了registry.type=nacosconfig.type=nacos,意思是:读取 Nacos配置中心的seata配置。但是Nacos配置中心又没有加 file.confregistry.conf的配置导致的….

我以为 V1.5.1版本的seata-server目录 D:\seata-server-1.5.1\seata\conf\application.yml配置文件配置了,就不用再在Nacos配置了,毕竟他们这么像…然而并不是,一个是服务端(seata-server)的配置,一个是客户端(微服务)的配置。特此记录一下,避免忘记。

方案1:改为从文件配置的方式获取配置

  1. 修改配置:registry.type=fileconfig.type=file ,意思是从文件配置的方式获取配置。
  2. 然后,客户端添加 file.confregistry.conf 配置,如下图:

1.5.0 ~ 1.5.1 版本关于(seata/script/client/conf/)配置的地址

配置目录示例图

方案2:“配置中心读取的方式”有个坑:除了添加自定义的seata.properties配置文件外, 还需要额外添加 DataId 为service.vgroupMapping.default_tx_group,值为default

比如:Nacos配置中心,Nacos 关于Seata客户端的配置,如下图:
  1. service.vgroupMapping.default_tx_group 。 这个配置是因为 RegistryService类默认创建了 key 为service.vgroupMapping.default_tx_group的监听器,专门监听这个属性,如果没添加,控制台会报错can not get cluster name in registry config 'service.vgroupMapping.default_tx_group', please make sure registry config correct
  2. seata-clien-file-conf.yml (相当于将 file.conf 文件迁移到配置中心。)

Nacos配置中心截图

application.yml (相当于将registry.conf配置文件的配置整合到这里 )
# Seata registry.conf 配置文件的配置
seata:
  enabled: true # 默认开启 (这个值配到配置中心也没用,所以放这里)
  application-id: ${spring.application.name}  #  (这个值配到配置中心也没用,所以放这里)
  tx-service-group: default_tx_group # 事务群组(可以每个应用独立取名,也可以使用相同的名字)
  registry:
    type: nacos  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
    nacos:
      server-addr: localhost:8848
      namespace: ee49309e-3d4b-431a-a8e6-06a7538dc95f
      cluster: default
      group: SEATA_GROUP
      username: nacos
      password: nacos

  config:
    type: nacos  # file、nacos 、apollo、zk、consul、etcd3
    nacos:
      namespace: ee49309e-3d4b-431a-a8e6-06a7538dc95f   # 配置中心名称空间唯一id
      server-addr: localhost:8848
      group: SEATA_GROUP   # nacos分组,默认是 SEATA_GROUP ,注意:Nacos默认组是 DEFAULT_GROP
      username: nacos
      password: nacos
      data-id:  seata-clien-file-conf.yml   # 自定义读取配置名,默认seata.properties
seata-clien-file-conf.yml(相当于file.conf配置文件)
# Seata file.conf 配置文件的配置
seata:
  client:
    rm-report-success-enable: true
    rm-table-meta-check-enable: false # 自动刷新缓存中的表结构(默认false)
    rm-report-retry-count: 5 # 一阶段结果上报TC重试次数(默认5)
    rm-async-commit-buffer-limit: 10000 # 异步提交缓存队列长度(默认10000)
    rm:
      lock:
        lock-retry-internal: 10 # 校验或占用全局锁重试间隔(默认10ms)
        lock-retry-times: 30 # 校验或占用全局锁重试次数(默认30)
        lock-retry-policy-branch-rollback-on-conflict: true # 分支事务与其它全局回滚事务冲突时锁策略(优先释放本地锁让回滚成功)
    tm-commit-retry-count: 3 # 一阶段全局提交结果上报TC重试次数(默认1次,建议大于1)
    tm-rollback-retry-count: 3 # 一阶段全局回滚结果上报TC重试次数(默认1次,建议大于1)
    undo:
      undo-data-validation: true # 二阶段回滚镜像校验(默认true开启)
      undo-log-serialization: jackson # undo序列化方式(默认jackson)
      undo-log-table: undo_log  # 自定义undo表名(默认undo_log)
    log:
      exception-rate: 100 # 日志异常输出概率(默认100)
    support:
      spring:
        datasource-autoproxy: true
  service:
    vgroup-mapping:
      default_tx_group: default # TC 集群(必须与seata-server保持一致)
    grouplist:
      default: 127.0.0.1:8091
    enable-degrade: false # 降级开关
    disable-global-transaction: false # 禁用全局事务(默认false)

  transport:
    type: TCP
    server: NIO
    heartbeat: true
    serialization: seata
    compressor: none
    enable-client-batch-send-request: true # 客户端事务消息请求是否批量合并发送(默认true)
    shutdown:
      wait: 3
    thread-factory:
      boss-thread-prefix: NettyBoss
      worker-thread-prefix: NettyServerNIOWorker
      server-executor-thread-prefix: NettyServerBizHandler
      share-boss-worker: false
      client-selector-thread-prefix: NettyClientSelector
      client-selector-thread-size: 1
      client-worker-thread-prefix: NettyClientWorkerThread

成功解决

  • seata-server 1.5.1 服务端控制台打印 内容如下,表示 该“xxx-order”服务已经成功注册到 seata。运行稳定后,客户端控制也没有再报错了,成功。

客户端成功注册到seata的截图

解决过程中的坑:

  1. 官方文档只有使用效果的大概说明,具体还得看“子项目”的代码。
  2. 期间,我还查看了官网的 版本升级指南 ,从 v1.4.x 升级到 v1.5.x 版本没有涉及到该问题的改动,没有找到该问题的答案。
  3. 一开始我下载的是最新版的v1.5.1,seata-server。然后被安装目录忽悠了。下面是 v1.5.1v1.4.2conf目录对比图。
    版本对比图
  4. 无奈之下,去 github找到官方提供的 示例代码子项目(seata-samples) ,仔细阅读说明,并跑了一下项目。
  5. 每一个子项目的配置文件都会有 “file.conf” 和“registry.conf”配置文件。后来才得知这些项目均是使用了 registry.type=fileconfig.type=file 从本地文件读取配置的方式获取配置 (即上面的方案1)。

总结:

  • 很显然,v1.4.2 使用的是从“本地文件读取(file.confregistry.conf)”的方式获取的配置,实际上这种方式里面配置的很多参数我们很多都不需要改动,或者说一般人也不会轻易修改;

  • 而 v1.5.1 版本其中一项优化应该是优化掉 file.confregistry.conf ,然后整合到 了 application.yml中,将大部分不会被改动的参数(使用默认配置的方式)隐藏起来。

  • 由于这种将配置放在 application.yml的方式里面放的配置跟 file.confregistry.conf配置很像,导致我有种错觉:服务端配置了,客户端就不用配置了 file.confregistry.conf,然后实际上并不是。seata-server 是服务端,服务端是服务端,客户端是客户端,两边的配置是分开的,都需要加。

  • 解决该问题的办法就是“添加seata配置( file.confregistry.conf)”。

问题2、SpringBoot 整合完 Seata 后,发现分布式事务并没有起作用?

请按照顺序排查是否有如下情况。

1、检查 依赖是否引入正确.只导入 seata-spring-boot-starter依赖是不够的。

  • 只导入 seata-spring-boot-starter依赖是不够的

  • 还需要导入 spring-boot-starter-aop依赖,内部有引入 org.aspectj:aspectjweaver

    建议这样引入依赖:

 
<dependency>
    <groupId>com.alibaba.cloudgroupId>
    <artifactId>spring-cloud-starter-alibaba-seataartifactId>
     
    <version>${spring-cloud-alibaba.version}version>
dependency>
 
<dependency>
    <groupId>io.seatagroupId>
    <artifactId>seata-spring-boot-starterartifactId>
    <version>1.5.1version>
dependency>

依赖图:

依赖图

2、如果是“从文件读取配置的方式(registry.type=fileconfig.type=file )”,检查 classes 目录下是否加载了配置文件。

如下图所示:

Classes加载的配置是否存在

3、@GlobalTransactional 注解是否起效果?

  1. 检查seata.enabled属性,设置为 true才是打开 seata 分布式事务。

  2. @GlobalTransactional是加在 大事务的入口方法上。比如 :A 服务的方法调用了 :B 、C、D服务的方法。则 @GlobalTransactional注解应该加在 A服务的 xxxxServiceImpl方法上,而不是加在 被远程调用的 B、C、D服务。

  3. @GlobalTransactional标注没问题,但是涉及到的远程调用微服务是否有引入 seata依赖? 涉及到的 各个微服务(B、C、D) 是否正常注册到seata-server事务协调器 ?

    • 如果正常注册上,seata-server控制台会打印:TM register success,message:RegisterTMRequest{applicationId='你的服务名'...略}
    • 还会打印RM register success,message:RegisterRMRequest{resourceIds='jdbc:mysql://ip:3306/数据库名',applicationId='你的服务名'...略}
  4. 各个服务需要使用seata的重复排查 1-3 问题,一般就能解决。

三、Seata AT 模式解决分布式事务的弊端

Seata 默认是使用“AT模式”解决分布式事务,其实就是“2PC模式” ,由于AT模式是基于全局事务实现的,基于一把全局锁,因此不适用高并发场景。

Seata 还提供支持了“TCC模式”和“saga模式”,虽然并发数相较于AT模式有改善,但遇到高并发场景,一样不太适用。

高并发的场景,最终还得利用“柔性事务-最大努力通知方案” 或者 “柔性事务-可靠消息+最终一致性方案(异步确保型)” 解决。

柔性事务-最大努力通知方案

按规律进行通知,不保证数据一定能通知成功,但会提供可查询操作接口进行核对

这种方案主要用在与第三方系统通讯时,比如:调用微信或支付宝支付后的支付结果通知。这种方案也是结合MQ进行实现,

例如:通过MQ发送 http 请求,设置最大通知次数。达到通知次数后即不再通知。

案例:银行通知、商户通知等(各大交易业务平台间的商户通知:多次通知、查询校对、对账文件),支付宝的支付成功异步回调

柔性事务-可靠消息+最终一致性方案(异步确保型)

实现:业务处理服务在业务事务提交之前,向实时消息服务请求发送消息,实时消息服务只记录消息数据,而不是真正的发送。业务处理服务在业务事务提交之后,向实时消息服务确认发送。只有在得到确认发送指令后,实时消息服务才会真正发送。

四、相关链接:

  • SpringCloud Alibab-Seata 官网
  • Seata 官网“快速入门”文档入口
  • Seata-Server 服务端GitHub下载地址
  • Seata-samples 子项目GitHub地址
  • v1.5.0 - v1.5.1 seata conf配置文件地址

你可能感兴趣的:(Spring,Cloud,Alibaba,java,spring,boot,seata)