分布式事務處理----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就邁出了一大步

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM