seata1.5.2

1. Seata是什么?
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和XA 事务模式,为用户打造一站式的分布式解决方案。AT模式是阿里首推的模式,阿里云上有商用版本的GTS(Global Transaction Service 全局事务服务)。

2. Seata的三大角色
在 Seata 的架构中,一共有三个角色:

TC (Transaction Coordinator) - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。
TM (Transaction Manager) - 事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务。
RM (Resource Manager) - 资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚


3. Seata的模式

  • AT
  • TCC
  • Sage
  • XA

这边就不过多说明,我们这边主要使用的是AT模式,这个也是seata开源版本的选择,基于2阶段提交的思想

官网:https://seata.io/zh-cn/index.html

源码: https://github.com/seata/seata

官方Demo: https://github.com/seata/seata-samples

 

4. 设计亮点

相比与其它分布式事务框架,Seata架构的亮点主要有几个:

  1. 应用层基于SQL解析实现了自动补偿,从而最大程度的降低业务侵入性;
  2. 将分布式事务中TC(事务协调者)独立部署,负责事务的注册、回滚;
  3. 通过全局锁实现了写隔离与读隔离。

5. 存在的问题

性能损耗

一条Update的SQL,则需要全局事务xid获取(与TC通讯)、before image(解析SQL,查询一次数据库)、after image(查询一 次数据库)、insert undo log(写一次数据库)、before commit(与TC通讯,判断锁冲突),这些操作都需要一次远程通讯RPC,而 且是同步的。另外undo log写入时blob字段的插入性能也是不高的。每条写SQL都会增加这么多开销,粗略估计会增加5倍响应时间。

性价比

为了进行自动补偿,需要对所有交易生成前后镜像并持久化,可是在实际业务场景下,这个是成功率有多高,或者说分布式事务失败 需要回滚的有多少比率?按照二八原则预估,为了20%的交易回滚,需要将80%的成功交易的响应时间增加5倍,这样的代价相比于让应 用开发一个补偿交易是否是值得?

全局锁

热点数据  相比XA,Seata 虽然在一阶段成功后会释放数据库锁,但一阶段在commit前全局锁的判定也拉长了对数据锁的占有时间,这个开销 比XA的prepare低多少需要根据实际业务场景进行测试。全局锁的引入实现了隔离性,但带来的问题就是阻塞,降低并发性,尤其是热点 数据,这个问题会更加严重。

回滚锁释放时间  Seata在回滚时,需要先删除各节点的undo log,然后才能释放TC内存中的锁,所以如果第二阶段是回滚,释放锁的时间会更长。

死锁问题  Seata的引入全局锁会额外增加死锁的风险,但如果出现死锁,会不断进行重试,最后靠等待全局锁超时,这种方式并不优雅,也延 长了对数据库锁的占有时间。

 

6. Seata快速开始 (默认nacos-server2.1.0已经已经安装配置好,我的nacos是安装在win10当中的,单机模式 127.0.0.1:8848)

1 Seata Server(TC)环境搭建

下载安装包 https://github.com/seata/seata/releases 下载1.5.2版本(各个版本安装有所不同,且seata版本要与alibaba版本对应 请知悉)

 解压seata1.5.2的安装包

 主要关注截图的 bin conf lib script 目录

bin 启动命令 (.sh为liunx .bat为win)

 conf 目录为配置文件目录 (日志配置这边就不多说了,)

 application.example.yml 这个为模版了,按照这个配置就行

 application.yml 默认的是使用内置的file方式启动的,可以启动,但只适合单机配置,就不记录这种方式了(当初接触seata0.9版本的时候,玩的就是file模式,需要修改的配置就很多,现在1.5.2版本都集成到配置文件上了,方便简化了很多)

我就直接上修改好的配置文件了

server:
  port: 7091 # 默认端口就不修改了

spring:
  application:
    name: seata-server # 默认服务名称 就不修改了

logging:
  config: classpath:logback-spring.xml # 日志配置
  file:
    path: ${user.home}/logs/seata
  extend:
    logstash-appender:
      destination: 127.0.0.1:4560
    kafka-appender:
      bootstrap-servers: 127.0.0.1:9092
      topic: logback_to_logstash

console: # 控制台的用户名密码
  user:
    username: seata
    password: seata

seata:
  config:
    # support: nacos 、 consul 、 apollo 、 zk  、 etcd3 seata的配置中心方式 nacos的配置中心就不多说 都是常规配置 指定了nacos的用户密码那配置文件需要写出来
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace:
      group: SEATA_GROUP
      username:
      password:
      ##if use MSE Nacos with auth, mutex with username/password attribute
      #access-key: ""
      #secret-key: ""
      data-id: seataServer.properties
  registry:
    # support: nacos 、 eureka 、 redis 、 zk  、 consul 、 etcd3 、 sofa
    type: nacos
    preferred-networks: 30.240.*
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace:
      cluster: default
      username:
      password:
      ##if use MSE Nacos with auth, mutex with username/password attribute
      #access-key: ""
      #secret-key: ""

  server:
    service-port: 8091 #If not configured, the default is '${server.port} + 1000' seata服务的配置 我这边都用的模版的默认配置
    max-commit-retry-timeout: -1
    max-rollback-retry-timeout: -1
    rollback-retry-timeout-unlock-enable: false
    enable-check-auth: true
    enable-parallel-request-handle: true
    retry-dead-threshold: 130000
    xaer-nota-retry-timeout: 60000
    vgroup-mapping:
      fsp_tx_group: default
    recovery:
      handle-all-session-period: 1000
    undo:
      log-save-days: 7
      log-delete-period: 86400000
    session:
      branch-async-queue-size: 5000 #branch async remove queue size
      enable-branch-async-remove: false #enable to asynchronous remove branchSession
  store:
    # support: file 、 db 、 redis seata数据的保存方式,这边后续考虑高可用 选择使用数据库保存 默认是file文件
    mode: db
    db:
      datasource: druid
      db-type: mysql
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://110.41.42.252:3308/seata_server?rewriteBatchedStatements=true
      user: root
      password: 123456
      min-conn: 5
      max-conn: 100
      global-table: global_table
      branch-table: branch_table
      lock-table: lock_table
      distributed-lock-table: distributed_lock
      query-limit: 100
      max-wait: 5000
  security:
    secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
    tokenValidityInMilliseconds: 1800000
    ignore:
      urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login

 lib/jdbc目录下有2个mysql的驱动包一个是5.X 一个是8.X 这边最删除掉不用的驱动包(mysql服务的版本查看这边就不多说了)

 

 新建一个数据库我这边叫 seata_server 与配置文件对应起来 在mysql服务中执行mysql.sql 会生成4张表

 表具体的作用就不过多说明

 

客户端连接的数据库需要增加日志表,方便数据的回滚,这边也记录下来 熟悉mysql的对undo.log 应该不陌生

-- 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(20)   NOT NULL COMMENT 'branch transaction id',
    `xid`           VARCHAR(100) 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 = utf8mb4 COMMENT ='AT transaction mode undo table';

接下来就可以启动.bat 文件了 liunx就是.sh

 前提是nacos已经启动完成,且配置nacos正确 seata1.5.2对应nacos版本为2.1.0 版本要对应起来

nacos

 seata

 到此 seata服务就启动完成了 后续就到整合使用了

 

控制台地址: Http://127.0.0.1:7091

 

 

客户端搭建

这边就不多说直接上代码

创建父maven工程

pom

<properties>
        <java.version>8</java.version>
        <!-- Spring Cloud -->
        <spring.cloud.version>Hoxton.SR12</spring.cloud.version>

        <!-- Spring Boot -->
        <spring-boot.version>2.3.12.RELEASE</spring-boot.version>
        <!-- Spring Cloud alibaba -->
        <project.version>2.2.9.RELEASE</project.version>
    </properties>

    <!--Spring Cloud Alibaba Version-->
    <!--2.2.9.RELEASE-->
    <!--Sentinel Version-->
    <!--1.8.5-->
    <!--Nacos Version-->
    <!--2.1.0-->
    <!--RocketMQ Version-->
    <!--4.9.4-->
    <!--seata Version-->
    <!--1.5.2-->
    <dependencies>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <dependencyManagement>
        <dependencies>

            <!-- Spring Dependencies -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring.cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.9.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

        </dependencies>
    </dependencyManagement>

事务分支1

pom

<dependencies>
        <!--seata 分布式事务组件-->
        <!--<dependency>-->
            <!--<groupId>com.alibaba.cloud</groupId>-->
            <!--<artifactId>spring-cloud-starter-alibaba-seata</artifactId>-->
        <!--</dependency>-->
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!-- SpringBoot整合Web组件+actuator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--数据库连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.6</version>
        </dependency>

        <!--MySQL驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- mybatisPlus 核心库 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>

        <!--seata 分布式事务组件-->
        <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>
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
            <version>1.5.2</version>
        </dependency>
    </dependencies>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

application.yml

server:
  port: 8090

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://110.41.42.252:3308/test003?useUnicode=true&characterEncoding=utf-8&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
  application:
    name: mybatis-service
  cloud:
    nacos:
      discovery:
        namespace: public
        server-addr: 127.0.0.1:8848
#开启日志
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

# 开放端口
management:
  endpoints:
    web:
      exposure:
        include: "*"
# seata日志级别
logging:
  level:
    io:
      seata: info
seata:
  registry:
    # 配置seata的注册中心, 告诉seata client 怎么去访问seata server(TC)
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848  # seata server 所在的nacos服务地址
      application: seata-server    # seata server 的服务名seata-server ,如果没有修改可以不配
      username: nacos
      password: nacos
      group: SEATA_GROUP          # seata server 所在的组,默认就是SEATA_GROUP,没有改也可以不配
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      group: SEATA_GROUP
  tx-service-group: fsp_tx_group  #这里每个服务都是对应不同的映射名,在配置中心可以看到
  service:
    vgroup-mapping:
      fsp_tx_group: default

最基础的代码就不写了

 

事务分支2 使用openfeign接口调用事务分支1

pom

<dependencies>
        <!--seata 分布式事务组件-->
        <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>
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
            <version>1.5.2</version>
        </dependency>

        <!--openfeign客户端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!-- SpringBoot整合Web组件+actuator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--数据库连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.6</version>
        </dependency>

        <!--MySQL驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- mybatisPlus 核心库 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>

    </dependencies>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

application.yml

server:
  port: 8092

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://110.41.42.252:3308/test002?useUnicode=true&characterEncoding=utf-8&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
  application:
    name: seata-service
  cloud:
    nacos:
      discovery:
        namespace: public
        server-addr: 127.0.0.1:8848
#开启日志
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

# 开放端口
management:
  endpoints:
    web:
      exposure:
        include: "*"
# seata日志级别
logging:
  level:
    com.nicong.feign: debug
    io:
      seata: info
# feign饥饿加载配置
ribbon:
  eager-load:
    enabled: true
    clients: mybatis-service
feign:
  client:
    config:
      mybatis-service: #服务名称
        connectTimeout: 5000 #默认2秒
        readTimeout: 10000 #默认5秒:
        loggerLevel: full

seata:
  registry:
    # 配置seata的注册中心, 告诉seata client 怎么去访问seata server(TC)
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848  # seata server 所在的nacos服务地址
      application: seata-server    # seata server 的服务名seata-server ,如果没有修改可以不配
      username: nacos
      password: nacos
      group: SEATA_GROUP          # seata server 所在的组,默认就是SEATA_GROUP,没有改也可以不配
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      group: SEATA_GROUP
  tx-service-group: fsp_tx_group  #这里每个服务都是对应不同的映射名,在配置中心可以看到
  service:
    vgroup-mapping:
      fsp_tx_group: default

启动类需要额外加上  

@EnableFeignClients 开启远程调用支持

具体的代码实现就不写了 基础性的东西

现在最重要的一步
给事务分组
按照配置文件中的
tx-service-group: fsp_tx_group  #这里每个服务都是对应不同的映射名,在配置中心可以看到
fsp_tx_group: default

登录到naocs中配置
service.vgroupMapping.fsp_tx_group:default

 保存发布就可以了

启动2个事务分支(确保naocs seata正常)

观查 seata窗口:

启动分支1 可以看到seata已经生成 TM RM维护分支

 

启动分支2 同样可以看到TM RM生成

 

上面提到过每个事务分支的数据库需要加上日志表的不要忘记,后续的演示就不截图了
在service 实现方法接口上加上 @GlobalTransactional 就可以了

使用feign接口调用另外一个接口如果报错,数据库的数据都会对应的进行回滚,基于日志表

打完收工