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接口調用另外一個接口如果報錯,數據庫的數據都會對應的進行回滾,基于日志表

打完收工