分佈式事務 —— SpringCloud Alibaba Seata

Seata 簡介

傳統的單體應用中,業務操作使用同一條連接操作不同的數據表,一旦出現異常就可以整體回滾。隨着公司的快速發展、業務需求的變化,單體應用被拆分成微服務應用,原來的單體應用被拆分成多個獨立的微服務,分別使用獨立的數據源,業務操作需要調用三個服務來完成。此時每個服務內部的數據一致性由本地事務來保證,但是全局的數據一致性問題無法保證。在微服務架構中,一次業務請求需要操作多個數據源或需要進行遠程調用,就會產生分佈式事務問題。

Seata 是一款開源的分佈式事務解決方案,致力於提供高性能和簡單易用的分佈式事務服務,Seata 爲用戶提供 AT、TCC、SAGA 和 XA 事務模式,爲用戶打造一站式的分佈式解決方案。AT 模式是 Seata 默認的工作模式,該模式是 XA 協議的演變。

關於分佈式事務的知識可以參考:https://zhuanlan.zhihu.com/p/263555694


Seata 服務端

通過 https://github.com/seata/seata/releases 地址下載 Seata 安裝包,本文使用的 Seata 版本是 seata-server-1.7.1

解壓 seata-server-1.7.1 安裝包,修改 conf/application.conf 文件

server:
  port: 7091

spring:
  application:
    name: seata-server

logging:
  config: classpath:logback-spring.xml
  file:
    path: D:\seata-server-1.7.1\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:
    # 使用nacos作爲配置中心,seata將從nacos獲取配置
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: 0178e474-2cfb-47c3-bded-da7cfa260f99
      group: springcloud-project
      data-id: seata-server.properties
  registry:
    # 使用nacos作爲註冊中心,seata將自身服務註冊到nacos
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: 0178e474-2cfb-47c3-bded-da7cfa260f99
      group: springcloud-project
      application: seata-server
      cluster: default
  store:
    # 存儲模式:
    # file模式爲單機模式,全局事務會話信息內存中讀寫並持久化本地文件 rootdata,性能較高
    # db模式爲高可用模式,全局事務會話信息通過db共享,相應性能會差
    # redis模式性能較高,存在事務信息丟失風險,需要提前配置適合當前場景的redis持久化配置
    mode: file
#  server:
#    service-port: 8091 #If not configured, the default is '${server.port} + 1000'
  security:
    secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
    tokenValidityInMilliseconds: 1800000
    ignore:
      urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login

在 Nacos 控制檯創建配置文件 seata-server.properties,注意分組和命名空間要與上述配置保持一致

具體配置項是從 script/config-center/config.txt 粘貼修改而來,這裏只使用對我們有用的配置,主要是數據庫配置信息

#Transaction storage configuration, only for the server.
store.mode=db
store.lock.mode=db
store.session.mode=db

#These configurations are required if the `store mode` is `db`.
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/test_db?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useAffectedRows=true
store.db.user=root
store.db.password=123
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.distributedLockTable=distributed_lock
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000

在上面配置的數據庫內,執行 script/server/db 目錄下的 sql 腳本,創建服務端所需的表

完成以後,即可進入 bin 目錄使用腳本啓動 Seata


Seata 客戶端

爲客戶端微服務添加依賴

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>

配置文件添加如下配置

seata:
  enabled: true
  tx-service-group: test-seata-group # 自定義事務組名稱,需要與下面service.vgroup-mapping中的一個映射保持一致
  service:
    vgroup-mapping:
      test-seata-group: default
  registry:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8847/nacos
      namespace: 0178e474-2cfb-47c3-bded-da7cfa260f99
      group: springcloud-project
      application: seata-server

Seata 默認使用 AT 模式,該模式需求每個客戶端庫內都存在一張 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;

在需要使用分佈式事務的方法上添加註解 @GlobalTransactional,當方法內發生異常時,就可以帶動所調用微服務進行回滾

@GlobalTransactional
public void create(Order order) {

    log.info("下單開始");
    orderDao.create(order);
    log.info("下單結束");

    log.info("開始扣減餘額");
    server02FeignClient.accountIncrUsed(order.getUserId(), order.getMoney());
    log.info("扣減餘額結束");

    log.info("開始扣減庫存");
    server03FeignClient.storageIncrUsed(order.getProductId(), order.getCount());
    log.info("扣減庫存結束");

    log.info("開始修改訂單狀態");
    orderDao.updateStatus(1, order.getId());
    log.info("修改訂單狀態結束");
}

配置完成,啓動服務,即可開始測試


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章