SpringCloudAlibaba項目之Seata分布式事務


SpringCloudAlibaba隨筆目錄

一、SpringCloudAlibaba項目之父工程搭建

二、SpringCloudAlibaba項目之Nacos搭建及服務注冊

三、SpringCloudAlibaba項目之生產者與消費者

四、SpringCloudAlibaba項目之Ribbon負載均衡

五、SpringCloudAlibaba項目之OpenFeign遠程調用

六、SpringCloudAlibaba項目之Nacos-config配置中心

七、SpringCloudAlibaba項目之Sentinel流量控制

八、SpringCloudAlibaba項目之Seata分布式事務

九、SpringCloudAlibaba項目之GateWay網關

十、SpringCloudAlibaba項目之SkyWalking鏈路

 

SpringCloudAlibaba項目之Seata分布式事務

1、分布式事務

   事務是數據庫的概念,數據庫事務(ACID:原子性、一致性、隔離性和持久性);

  分布式事務的產生,是由於數據庫的拆分和分布式架構(微服務)帶來的,在常規情況下,我們在一個進程中操作一個數據庫,這屬於本地事務,如果在一個進程中操作多個數據庫,或者在多個進程中操作一個或多個數據庫,就產生了分布式事務;

(1)數據庫分庫分表就產生了分布式事務;

 (2)項目拆分服務化也產生了分布式事務;

2、Seata簡介

  Seata 是一款開源的分布式事務解決方案,致力於在微服務架構下提供高性能和簡單易用的分布式事務服務。

  Seata為用戶提供了AT、TCC、SAGA和XA事務模式,為用戶打造一站式的分布式解決方案;

四種事務模式中,XA模式正在開發中...,其他事務模式已經實現;

目前使用的流行度情況是:AT > TCC > Saga;

我們可以參看seata各公司使用列表:

https://github.com/seata/seata/issues/1246 大部分公司都采用的AT事務模式;

Seata已經在國內很多團隊開始落地,其中不乏有大公司;

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

官網文檔:https://seata.io/zh-cn/docs/overview/what-is-seata.html

在Seata的架構中,一共有三個角色:

TC (Transaction Coordinator) - 事務協調者

維護全局和分支事務的狀態,驅動全局事務提交或回滾;

TM (Transaction Manager) - 事務管理器

定義全局事務的范圍:開始全局事務、提交或回滾全局事務;

RM (Resource Manager) - 資源管理器

管理分支事務處理的資源,與TC交互以注冊分支事務和報告分支事務的狀態,並驅動分支事務提交或回滾;

其中TC為單獨部署的 Server 服務端,TM和RM為嵌入到應用中的 Client 客戶端;

在Seata中,一個分布式事務的生命周期如下:

TM請求TC開啟一個全局事務,TC會生成一個XID作為該全局事務的編號,XID會在微服務的調用鏈路中傳播,保證將多個微服務的子事務關聯在一起;

RM請求TC將本地事務注冊為全局事務的分支事務,通過全局事務的XID進行關聯;

TM請求TC告訴XID對應的全局事務是進行提交還是回滾;

TC驅動RM將XID對應的自己的本地事務進行提交還是回滾;

3、Seata服務端部署(TC事務協調者)

  因為TC需要進行全局事務和分支事務的記錄,所以需要對應的存儲,目前,TC有三種存儲模式( store.mode ):

file模式:適合單機模式,全局事務會話信息在內存中讀寫,並持久化本地文件 root.data,性能較高;

db模式:(mysql 5.7+)適合集群模式,全局事務會話信息通過 db 共享,相對性能差點;

redis模式:解決db存儲的性能問題;

 這里使用windows系統演示db模式;

(1)下載安裝包

seata下載地址:https://github.com/seata/seata/releases

(2)修改配置文件

 

 (3)創建數據庫表

 官網已經為我們創建好了腳本,只需要引入使用即可:https://github.com/seata/seata/tree/1.4.0/script/server/db

 運行mysql.sql文

-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS `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`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
    `branch_id`         BIGINT       NOT NULL,
    `xid`               VARCHAR(128) NOT NULL,
    `transaction_id`    BIGINT,
    `resource_group_id` VARCHAR(32),
    `resource_id`       VARCHAR(256),
    `branch_type`       VARCHAR(8),
    `status`            TINYINT,
    `client_id`         VARCHAR(64),
    `application_data`  VARCHAR(2000),
    `gmt_create`        DATETIME(6),
    `gmt_modified`      DATETIME(6),
    PRIMARY KEY (`branch_id`),
    KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
    `row_key`        VARCHAR(128) NOT NULL,
    `xid`            VARCHAR(96),
    `transaction_id` BIGINT,
    `branch_id`      BIGINT       NOT NULL,
    `resource_id`    VARCHAR(256),
    `table_name`     VARCHAR(32),
    `pk`             VARCHAR(36),
    `gmt_create`     DATETIME,
    `gmt_modified`   DATETIME,
    PRIMARY KEY (`row_key`),
    KEY `idx_branch_id` (`branch_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

總共三張表,global_table(存儲全局會話數據)、branch_table(存儲分支會話數據)、lock_table(存儲鎖數據)

4、Seata服務端搭建-nacos

 (1)修改注冊中心

 (2)修改配置中心

 (3)啟動seata

 5、Seata客戶端搭建

  (1)pom.xml文件引入依賴

<!-- nacos 服務注冊發現(客戶端)依賴 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- nacos-config 配置中心依賴 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- seata 分布式事務依賴 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>

接口調用需要openfeign依賴

<!-- openfeign 遠程調用依賴 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

  (2)各微服務對應的數據庫中添加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,
    PRIMARY KEY(`id`),
    UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8

  (3)將配置注冊到nacos注冊中心

  到https://github.com/seata/seata  下,先將整個文件下載下來,將內部的script腳本文件復制一份到seata的根目錄下

 修改內部script\config\config.txt文件內容

關於config.txt文件中vgroupMapping.default_tx_group和在項目里配置的要一致

 執行script\config-center\nacos\nacos-config.sh把config.txt文件導入到nacos配置中心

注意:執行出現找不到config.txt的話修改一下nacos-config.sh里面關於config.txt的路徑指向你本地的即可。如果電腦無法運行.sh文件,安裝git即可。

 注冊成功

(3)各微服務添加配置

application.properties

#配置seata的注冊中心
seata.enabled=true
seata.application-id=${spring.application.name}
#配置事務分組
seata.tx-service-group=default_tx_group
seata.registry.type=nacos
seata.registry.nacos.server-addr=127.0.0.1:8848
seata.registry.nacos.application=seata-server
seata.registry.nacos.namespace=
seata.registry.nacos.username=nacos
seata.registry.nacos.password=nacos
seata.registry.nacos.group=SEATA_GROUP
#配置seata的配置中心
seata.config.type=nacos
seata.config.nacos.server-addr=127.0.0.1:8848
seata.config.nacos.namespace=
seata.config.nacos.username=nacos
seata.config.nacos.password=nacos
seata.config.nacos.group=SEATA_GROUP

 消費者

 OrderController

/**
 * 訂單服務
 */
@RestController
@RequestMapping("/order")
public class OrderController {

    @Resource
    private StockOpenFeignService stockOpenFeignService;

    @Resource
    private OrderService orderService;

    /**
     * 新增訂單
     * @return
     */
    @RequestMapping("/addOrder")
    @GlobalTransactional //分布式事務注解,這個一般放在業務層,這里圖方便
    public String addOrder(){
        Order order = new Order("襪子");
        int id = orderService.addOrder(order);
        System.out.println("訂單新增成功-id:" + id);
        //調用庫存扣減
        String result = stockOpenFeignService.subStock(id);
        return new Result(200,"訂單服務-訂單新增成功",result).toString();
    }
}
StockOpenFeignService
/**
 * 庫存服務接口
 * name:指定調用rest接口所對應的服務名
 * path:指定調用rest接口所在的StockController指定的@RequestMapping
 */
@FeignClient(name = "service-seata-stock",path = "stock")
public interface StockOpenFeignService {

    //聲明需要調用的rest接口對應的方法
    /**
     * 庫存扣減
     * @return
     */
    @PostMapping("/subStock")
    String subStock(int id);
}

生產者

 StockController

/**
 * 庫存服務
 */
@RestController
@RequestMapping("/stock")
public class StockController {

    @Value("${server.port}")
    private String port;

    @Resource
    private StockService stockService;/**
     * 庫存扣減
     * @return
     */
    @PostMapping("/subStock")
    public String subStock(@RequestBody int id) {
        System.out.println("進入庫存扣減接口");int num = stockService.subStock(id);
        Result result = null;
        if(num > 0){
            System.out.println("庫存扣減成功-id:" + id);
            result = new Result(200,"扣減庫存服務-庫存成功");
        }else {
            result = new Result(201,"扣減庫存服務-庫存失敗");
        }
        return result.toString();
    }
}

總結: 實踐是檢驗真理的唯一標准。

參考鏈接:https://blog.csdn.net/calonmo/article/details/106630754


免責聲明!

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



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