架構設計 | 基於Seata中介軟體,微服務模式下事務管理

知了一笑發表於2020-09-14

原始碼地址:GitHub·點這裡 || GitEE·點這裡

一、Seata簡介

1、Seata元件

Seata是一款開源的分散式事務解決方案,致力於提供高效能和簡單易用的分散式事務服務。Seata將為使用者提供了AT、TCC、SAGA、XA事務模式,為使用者打造一站式的分散式解決方案。

2、支援模式

AT 模式

  • 基於支援本地 ACID 事務的關係型資料庫。
  • Java應用,通過 JDBC 訪問資料庫。

一階段:業務資料和回滾日誌記錄在同一個本地事務中提交,釋放本地鎖和連線資源。

二階段:提交非同步化,非常快速地完成。回滾通過一階段的回滾日誌進行反向補償。

TCC模式

一個分散式的全域性事務,整體是兩階段提交的模型,全域性事務是由若干分支事務組成的,分支事務要滿足兩階段提交的模型要求,即需要每個分支事務都具備自己的:

一階段 prepare 行為

二階段 commit 或 rollback 行為

Saga模式

Saga模式是SEATA提供的長事務解決方案,在Saga模式中,業務流程中每個參與者都提交本地事務,當出現某一個參與者失敗則補償前面已經成功的參與者,一階段正向服務和二階段補償服務都由業務開發實現。

XA模式

XA是一個分散式事務協議,對業務無侵入的分散式事務解決方案,XA提交協議需要事務參與者的資料庫支援,XA事務具有強一致性,在兩階段提交的整個過程中,一直會持有資源的鎖,效能不理想的缺點很明顯。

二、服務端部署

1、下載元件包

1.2版本:seata-server-1.2.0.zip

解壓目錄

  • bin:存放服務端執行啟動指令碼;
  • lib:存放服務端依賴的資源jar包;
  • conf:配置檔案目錄。

2、修改配置

file.conf配置

mode:db 即使用資料庫儲存事務資訊,這裡還可以選擇file儲存方式。

file模式為單機模式,全域性事務會話資訊記憶體中讀寫並持久化本地檔案root.data,效能較高;

db模式為高可用模式,全域性事務會話資訊通過db共享,相應效能差些;

redis模式Seata-Server 1.3及以上版本支援,效能較高,存在事務資訊丟失風險,請提前配置合適當前場景的redis持久化配置.

store {
  ## store mode: file、db
  mode = "db"
  db {
    datasource = "druid"
    dbType = "mysql"
    driverClassName = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://127.0.0.1:3306/seata_server"
    user = "root"
    password = "123456"
    minConn = 5
    maxConn = 30
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
    maxWait = 5000
  }
}

registry.conf配置

這裡選擇eureka作為註冊中心,seata-server也要作為一個服務新增到註冊中心,不使用配置中心所以config配置預設即可。

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "eureka"

  eureka {
    serviceUrl = "http://localhost:8761/eureka"
    application = "default"
    weight = "1"
  }
}

3、事務管理表

需要在seata-server即上述配置的MySQL庫中建立3張事務管理表:

  • 全域性事務:global_table
  • 分支事務:branch_table
  • 全域性鎖:lock_table
  • 事務回滾:undo_log
  • SQL指令碼:mysql-script目錄

4、啟動命令

Linux環境:sh seata-server.sh

三、業務服務搭建

1、程式碼結構

  • seata-eureka:註冊中心
  • seata-order:訂單服務
  • seata-account:賬戶服務
  • seata-inventor:庫存服務
  • seata-client:客戶端服務
  • account-feign:賬戶Feign介面
  • inventory-feign:庫存Feign介面
  • order-feign:訂單Feign介面

請求鏈路:客戶端->訂單->賬戶+庫存,測試整個流程的分散式事務問題。

2、資料庫結構

  • seata_server:seata元件服務端依賴庫
  • seata_account:模擬賬戶資料庫
  • seata_inventor:模擬庫存資料庫
  • seata_order:模擬訂單資料庫

各個庫指令碼位置:mysql-script/data-biz.sql

3、啟動服務

依次啟動:註冊中心,庫存服務,賬戶服務,訂單服務,客戶端服務;

Eureka服務列表如下:

四、Seata用法詳解

1、Seata基礎配置

幾個基礎服務的配置方式一樣。

conf配置

file.conf重點關注下面內容,事務組的名稱,需要在yml檔案中使用。

my_test_tx_group = "default"

registry.conf:是註冊中心的選擇。

2、資料庫配置

注意這裡的事務組名稱配置。

spring:
  # 事務組的名稱
  cloud:
    alibaba:
      seata:
        tx-service-group: my_test_tx_group
  # 資料來源配置
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      driverClassName: com.mysql.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/seata_account
      username: root
      password: 123456

將資料庫整體由Seata進行代理管理,核心API:DataSourceProxy。

@Configuration
public class SeataAccountConfig {

    @Value("${spring.application.name}")
    private String applicationName;

    @Bean
    public GlobalTransactionScanner globalTransactionScanner() {
        return new GlobalTransactionScanner(applicationName, "test-tx-group");
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.druid")
    public DruidDataSource druidDataSource() {
        return new DruidDataSource() ;
    }

    @Primary
    @Bean("dataSource")
    public DataSourceProxy dataSourceProxy(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();
    }
}

3、業務程式碼

核心註解:GlobalTransactional,管理整體的分散式事務。

@Service
public class OrderServiceImpl implements OrderService {
    private final Logger LOGGER = LoggerFactory.getLogger(OrderServiceImpl.class);

    @Resource
    private OrderMapper orderMapper ;
    @Resource
    private AccountFeign accountFeign ;
    @Resource
    private InventoryFeign inventoryFeign ;

    @GlobalTransactional
    @Override
    public Integer createOrder(String orderNo) {
        LOGGER.info("Order 生成中 "+orderNo);
        // 本服務下訂單庫
        Integer insertFlag = orderMapper.insert(orderNo) ;
        // 基於feign介面處理賬戶和庫存
        accountFeign.updateAccount(10L) ;
        inventoryFeign.updateInventory(10) ;
        return insertFlag ;
    }
}

測試流程:在任意服務下丟擲異常,觀察整體的事務狀態,觀察是否有整體的事務控制效果。

五、原始碼地址

GitHub地址:知了一笑
https://github.com/cicadasmile/spring-cloud-base
GitEE地址:知了一笑
https://gitee.com/cicadasmile/spring-cloud-base

推薦閱讀:架構設計系列

標題
架構設計:單服務.叢集.分散式,基本區別和聯絡
架構設計:分散式業務系統中,全域性ID生成策略
架構設計:分散式系統排程,Zookeeper叢集化管理
架構設計:介面冪等性原則,防重複提交Token管理
架構設計:快取管理模式,監控和記憶體回收策略
架構設計:非同步處理流程,多種實現模式詳解
架構設計:高併發流量削峰,共享資源加鎖機制
架構設計:分散式服務,庫表拆分模式詳解
架構設計:分散式事務①概念簡介和基礎理論
架構設計:基於電商交易流程,圖解TCC事務分段提交
架構設計:基於訊息中介軟體,圖解柔性事務一致性

相關文章