1. 程式人生 > 實用技巧 >宇智波程式筆記3-Spring Cloud同步場景分散式事務怎樣做?試試Seata

宇智波程式筆記3-Spring Cloud同步場景分散式事務怎樣做?試試Seata

一、概述

在微服務架構下,雖然我們會盡量避免分散式事務,但是隻要業務複雜的情況下這是一個繞不開的問題,如何保證業務資料一致性呢?本文主要介紹同步場景下使用SeataAT模式來解決一致性問題。

Seata是阿里巴巴開源的一站式分散式事務解決方案中介軟體,以高效並且對業務0 侵入的方式,解決微服務場景下面臨的分散式事務問題

二、Seata介紹

整體事務邏輯是基於兩階段提交的模型,核心概念包括以下3個角色:

  • TM:事務的發起者。用來告訴 TC,全域性事務的開始,提交,回滾。

  • RM:具體的事務資源,每一個 RM 都會作為一個分支事務註冊在 TC。

  • TC:事務的協調者seata-server,用於接收我們的事務的註冊,提交和回滾。

目前的Seata有兩種模式可使用分別對應不同業務場景

2.1. AT模式

該模式適合的場景:

  • 基於支援本地ACID事務的關係型資料庫。

  • Java 應用,通過JDBC訪問資料庫。


一個典型的分散式事務過程:

  1. TMTC申請開啟一個全域性事務,全域性事務建立成功並生成一個全域性唯一的XID

  2. XID在微服務呼叫鏈路的上下文中傳播。

  3. RMTC註冊分支事務,將其納入 XID 對應全域性事務的管轄。

  4. TMTC發起針對XID的全域性提交或回滾決議。

  5. TC排程XID下管轄的全部分支事務完成提交或回滾請求。

2.2. MT模式

該模式邏輯類似TCC

,需要自定義實現preparecommitrollback的邏輯,適合非關係型資料庫的場景

三、Seata場景樣例

模擬一個簡單的使用者下單場景,4個子工程分別是Bussiness(事務發起者)、Order(建立訂單)、Storage(扣減庫存)和Account(扣減賬戶餘額)

3.1. 部署Seata的Server端

Discover註冊、Config配置和Store儲存模組預設都是使用file只能適用於單機,我們安裝的時候分別改成使用nacosMysql以支援server端叢集

3.1.1. 下載最新版本並解壓

https://github.com/seata/seata/releases

3.1.2. 修改 conf/registry.conf 配置

註冊中心和配置中心預設是file這裡改為nacos;設定registry和config節點中的typenacos,修改serverAddr為你的nacos節點地址。

registry {
  type = "nacos"

  nacos {
    serverAddr = "192.168.28.130"
    namespace = "public"
    cluster = "default"
  }
}

config {
  type = "nacos"

  nacos {
    serverAddr = "192.168.28.130"
    namespace = "public"
    cluster = "default"
  }
}

3.1.3. 修改 conf/nacos-config.txt配置

  • 修改service.vgroup_mapping為自己應用對應的名稱;如果有多個服務,新增相應的配置

    預設組名為${spring.application.name}-fescar-service-group,可通過spring.cloud.alibaba.seata.tx-service-group配置修改

  • 修改store.mode為db,並修改資料庫相關配置

3.1.4. 初始化seata的nacos配置

cd conf
sh nacos-config.sh 192.168.28.130

成功後在nacos的配置列表中能看到seata的相關配置

3.1.5. 初始化資料庫

執行conf/db_store.sql中的指令碼

3.1.6. 啟動seata-server

sh bin/seata-server.sh -p 8091 -h 192.168.28.130

3.2. 應用配置

3.2.1. 初始化資料庫

執行指令碼seata-demo.sql

CREATE DATABASE `seata-demo` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE `seata-demo`;

--==========================回滾日誌表==========================
-- 此指令碼必須初始化在你當前的業務資料庫中,用於AT 模式XID記錄。與server端無關(注:業務資料庫)
drop table `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;


--==========================業務模擬表==========================
DROP TABLE IF EXISTS `storage_tbl`;
CREATE TABLE `storage_tbl` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `commodity_code` varchar(255) DEFAULT NULL,
  `count` int(11) DEFAULT 0,
  PRIMARY KEY (`id`),
  UNIQUE KEY (`commodity_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


DROP TABLE IF EXISTS `order_tbl`;
CREATE TABLE `order_tbl` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` varchar(255) DEFAULT NULL,
  `commodity_code` varchar(255) DEFAULT NULL,
  `count` int(11) DEFAULT 0,
  `money` int(11) DEFAULT 0,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


DROP TABLE IF EXISTS `account_tbl`;
CREATE TABLE `account_tbl` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` varchar(255) DEFAULT NULL,
  `money` int(11) DEFAULT 0,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


-- 初始化庫存模擬資料
INSERT INTO storage_tbl (www.jintianxuesha.com www.yuntianyul.com www.baihuayl7.cn id, commodity_code, count) VALUES (1, 'P001', 9999999);
INSERT INTO account_tbl (id, user_id, money) VALUES ('1', 'U001', 10000);

需在業務相關的資料庫中新增undo_log表,用於儲存需要回滾的資料

3.2.2. 新增registry.conf配置

直接把seata-server中的registry.conf複製到每個服務中去即可,不需要修改

3.2.3. 修改配置

demo中的每個服務各自修改配置檔案

  • bootstrap.yml修改nacos地址

  • application.yml修改資料庫配置

3.2.4. 配置資料來源代理

Seata是通過代理資料來源實現分散式事務,所以需要配置io.seata.rm.datasource.DataSourceProxyBean,且是@Primary預設的資料來源,否則事務不會回滾,無法實現分散式事務

public class DataSourceProxyConfig www.yifayuled.cn{
    www.yachengyl.cn www.yixingylzc.cn www.qiaoheibpt.com www.baishenjzc.cn
    @ConfigurationProperties(prefix = "spring.datasource")
    public DruidDataSource druidDataSource(www.lafei6d.cn) {
        return new DruidDataSource();
    }

    @Primary
    @Bean
    public DataSourceProxy dataSourceProxy(www.yixinpt2.cn DruidDataSource druidDataSource) {
        return new DataSourceProxy(druidDataSource);
    }
}

因為使用了mybatis的starter所以需要排除DataSourceAutoConfiguration,不然會產生迴圈依賴

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})

3.2.5. 事務發起者新增全域性事務註解

事務發起者business-service新增@GlobalTransactional註解

@GlobalTransactional
public void placeOrder(String userId) www.javachenglei.com{
    ......
}

3.3. 測試

提供兩個介面測試

  1. 事務成功:扣除庫存成功 > 建立訂單成功 > 扣減賬戶餘額成功
    http://localhost:9090/placeOrder

  2. 事務失敗:扣除庫存成功 > 建立訂單成功 > 扣減賬戶餘額失敗,事務回滾
    http://www.lecaixuangj.cn localhost:9090/placeOrderFallBack

3.4. demo地址

https://www.hongtuuzhuc.cn gitee.com/zlt2000/microservices-platform/tree/master/zlt-demo/seata-demo

往期精彩回顧

  • 日誌排查問題困難?分散式日誌鏈路跟蹤來幫你

  • Spring Cloud Zuul的動態路由怎樣做?整合Nacos實現很簡單

  • 限流怎麼做?閘道器zuul整合Sentinel最新的閘道器流控元件

  • 為什麼我的Spring Boot自定義配置項在IDE裡面不會自動提示?

  • 阿里註冊中心Nacos生產部署方案

  • Spring Cloud開發人員如何解決服務衝突和例項亂竄?