天天看點

SpringCloud 整合分布式事務元件 Seata (八)前言正文

前言
SpringCloud 整合分布式事務元件 Seata (八)前言正文

近期一直在忙項目,我也是打工仔。不多說,我們開始玩一玩seata。

正文

什麼都不說,我們按照慣例,先上一個圖(圖裡不規範的使用請忽略):

SpringCloud 整合分布式事務元件 Seata (八)前言正文

簡單一眼就看出來, 比我們平時用的東西,多了  Seata Server 微服務 。

同樣這個 Seata Server 微服務  ,也是需要注冊到eureka上面去的。 

那麼我們首先就搞一搞這個 seata server ,那麼剩下的就是一些原本的業務服務整合配置了。

該篇用的 seata server 版本,用的是1.4.1 , 可以去git下載下傳下。

當然,我也是給你們備了的:

seata server 1.4.1

某度網盤分享位址:

https://pan.baidu.com/s/1R9McfkSkoj72Pf98ugCvBw

提取碼:

4ynp

第一步,下載下傳下來解壓 :

SpringCloud 整合分布式事務元件 Seata (八)前言正文

第二步,創個 seata server 用的資料庫 :

CREATE TABLE `branch_table` (
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(128) NOT NULL,
  `transaction_id` bigint(20) DEFAULT NULL,
  `resource_group_id` varchar(32) DEFAULT NULL,
  `resource_id` varchar(256) DEFAULT NULL,
  `branch_type` varchar(8) DEFAULT NULL,
  `status` tinyint(4) DEFAULT NULL,
  `client_id` varchar(64) DEFAULT NULL,
  `application_data` varchar(2000) DEFAULT NULL,
  `gmt_create` datetime DEFAULT NULL,
  `gmt_modified` datetime DEFAULT NULL,
  PRIMARY KEY (`branch_id`),
  KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
CREATE TABLE `global_table` (
  `xid` varchar(128) NOT NULL,
  `transaction_id` bigint(20) DEFAULT NULL,
  `status` tinyint(4) NOT NULL,
  `application_id` varchar(32) DEFAULT NULL,
  `transaction_service_group` varchar(32) DEFAULT NULL,
  `transaction_name` varchar(128) DEFAULT NULL,
  `timeout` int(11) DEFAULT NULL,
  `begin_time` bigint(20) DEFAULT NULL,
  `application_data` varchar(2000) DEFAULT NULL,
  `gmt_create` datetime DEFAULT NULL,
  `gmt_modified` datetime DEFAULT NULL,
  PRIMARY KEY (`xid`),
  KEY `idx_gmt_modified_status` (`gmt_modified`,`status`),
  KEY `idx_transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
CREATE TABLE `lock_table` (
  `row_key` varchar(128) NOT NULL,
  `xid` varchar(96) DEFAULT NULL,
  `transaction_id` bigint(20) DEFAULT NULL,
  `branch_id` bigint(20) NOT NULL,
  `resource_id` varchar(256) DEFAULT NULL,
  `table_name` varchar(32) DEFAULT NULL,
  `pk` varchar(36) DEFAULT NULL,
  `gmt_create` datetime DEFAULT NULL,
  `gmt_modified` datetime DEFAULT NULL,
  PRIMARY KEY (`row_key`),
  KEY `idx_branch_id` (`branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
           

建立完後:

SpringCloud 整合分布式事務元件 Seata (八)前言正文

第三步,修改下 \seata-1.4.1\seata-server-1.4.1\seata\conf 裡的配置檔案一些資訊 :

1. registry.conf

SpringCloud 整合分布式事務元件 Seata (八)前言正文
SpringCloud 整合分布式事務元件 Seata (八)前言正文
SpringCloud 整合分布式事務元件 Seata (八)前言正文

ok,registry.conf 這檔案就修改這些配置項。

2. file.conf :

SpringCloud 整合分布式事務元件 Seata (八)前言正文

以上兩個檔案配置完(記得儲存), 我們先把我們的注冊中心 eureka服務跑起來,然後點選啟動 seata server:

SpringCloud 整合分布式事務元件 Seata (八)前言正文

可以看到啟動成功(前提是eureka已經啟動):

SpringCloud 整合分布式事務元件 Seata (八)前言正文

第三步 ,配置我們需要用到 分布式事務 seata元件的 微服務 :

我這裡的示例實踐,需要用到的有2個微服務 :

SpringCloud 整合分布式事務元件 Seata (八)前言正文

那麼我們這兩個微服務都需要做點什麼呢?

1. 在對應的微服務的對應的不同資料庫裡(隻要你想用上seata的), 都加上undo_log 這個表:

SpringCloud 整合分布式事務元件 Seata (八)前言正文

SQL語句:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for undo_log
-- ----------------------------
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) NOT NULL,
  `log_created` datetime(0) NULL,
  `log_modified` datetime(0) NULL,
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;
           

2. 關鍵步驟了 , 導入jar包 (需要用到seata元件都服務都需要導入)

<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>
			<artifactId>seata-all</artifactId>
			<groupId>io.seata</groupId>
			<version>1.4.1</version>
		</dependency>
           

3.更加關鍵了,就是做配置

先總得了解下,需要用到seata的 微服務需要做些什麼配置 ?

1. 在resources 下 新增2個配置檔案 , file.conf  和   registry.conf

2.yml 新增配置seata 事務組參數

3.代碼調整資料源代理,交給seata代理

1.   registry.conf  

SpringCloud 整合分布式事務元件 Seata (八)前言正文
SpringCloud 整合分布式事務元件 Seata (八)前言正文

以上是業務微服務裡的registry.conf  需要改動的配置資訊 ,給出一份該篇文章使用的:

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "eureka"
  loadBalance = "RandomLoadBalance"
  loadBalanceVirtualNodes = 10

  nacos {
    application = "seata-server"
    serverAddr = "127.0.0.1:8848"
    group = "SEATA_GROUP"
    namespace = ""
    cluster = "default"
    username = ""
    password = ""
  }
  eureka {
    serviceUrl = "http://localhost:8761/eureka/"
    application = "seata-server"
    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 = "127.0.0.1:8848"
    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"
    apolloAccesskeySecret = ""
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    sessionTimeout = 6000
    connectTimeout = 2000
    username = ""
    password = ""
  }
  etcd3 {
    serverAddr = "http://localhost:2379"
  }
  file {
    name = "file.conf"
  }
}
           

file.conf

SpringCloud 整合分布式事務元件 Seata (八)前言正文

給出一份該篇使用的完整的:

transport {
  # tcp udt unix-domain-socket
  type = "TCP"
  #NIO NATIVE
  server = "NIO"
  #enable heartbeat
  heartbeat = true
  # the client batch send request enable
  enableClientBatchSendRequest = true
  #thread factory for netty
  threadFactory {
    bossThreadPrefix = "NettyBoss"
    workerThreadPrefix = "NettyServerNIOWorker"
    serverExecutorThread-prefix = "NettyServerBizHandler"
    shareBossWorker = false
    clientSelectorThreadPrefix = "NettyClientSelector"
    clientSelectorThreadSize = 1
    clientWorkerThreadPrefix = "NettyClientWorkerThread"
    # netty boss thread size,will not be used for UDT
    bossThreadSize = 1
    #auto default pin or 8
    workerThreadSize = "default"
  }
  shutdown {
    # when destroy server, wait seconds
    wait = 3
  }
  serialization = "seata"
  compressor = "none"
}
service {
#這裡注意,等号前後都是配置,前面是yml裡配置的事務組,後面是register.conf裡定義的seata-server
  vgroupMapping.test_tx_group = "seata-server"
  #only support when registry.type=file, please don't set multiple addresses
  seata_tc_server.grouplist = "127.0.0.1:8091"
  #degrade, current not support
  enableDegrade = false
  #disable seata
  disableGlobalTransaction = false
}
client {
  rm {
    asyncCommitBufferLimit = 10000
    lock {
      retryInterval = 10
      retryTimes = 30
      retryPolicyBranchRollbackOnConflict = true
    }
    reportRetryCount = 5
    tableMetaCheckEnable = false
    reportSuccessEnable = false
  }
  tm {
    commitRetryCount = 5
    rollbackRetryCount = 5
  }
  undo {
    dataValidation = true
    logSerialization = "jackson"
    logTable = "undo_log"
  }
  log {
    exceptionRate = 100
  }
}
           

2. 需要在yml配置檔案加上配置項,指明目前服務使用了 seata分布式事務元件,且需要加入的分布式事務組是哪個: 

SpringCloud 整合分布式事務元件 Seata (八)前言正文
spring:
  cloud:
    alibaba:
      seata.tx-service-group: test_tx_group
           

3.然後是需要将資料源交給seata去代理:

去掉預設自動加載資料源

配置dao層掃描位置

SpringCloud 整合分布式事務元件 Seata (八)前言正文
@MapperScan("com.cloud.client1.dao")
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
           

然後是seata代理資料源(紅色标注的地方需要注意下,都是實打實踩出來的坑。導入類的來源以及掃描的mapper的xml位置):

SpringCloud 整合分布式事務元件 Seata (八)前言正文

ok,到這裡一個微服務 usercent的 整合 seata 分布式元件算是完成了。 

同樣,我們繼續對 另外一個微服務 coredata 做seata 分布式元件  整合 。

也是第一步,file.conf 和 registry.conf  (其實我們現在就是讓這兩個微服務在同一個分布式事務組,我們直接把剛才在usercent那邊的兩個檔案直接粘貼過來就好)。

然後第二步就是修改yml配置:

SpringCloud 整合分布式事務元件 Seata (八)前言正文

第三步就是新增seata資料源代理、application上的注解排除自動加載資料源和掃描dao層位址,yml上面的配置項:

SpringCloud 整合分布式事務元件 Seata (八)前言正文

完事,兩個業務微服務都整合了 分布式事務元件 seata ,而且都設定在同個分布式事務組裡 (test_tx_group).

那麼到這其實已經完事了, 接下來玩下 分布式事務復原場景 的例子 (怎麼去使用)。

PS: 使用方式關注 @GlobalTransactional  注解, 想把全局事務從哪裡開啟,就把這個注解放到哪個方法上去, 這個方法内 調用的其他服務隻要都使用了seata,且在同個事務組,那麼就會加入到目前的全局事務裡。 

我該篇就不弄太多服務了,就弄了2個分布式服務。   

展示的例子内容 :

1. 上遊服務 出錯, 觸發分布式事務, 上下遊服務都會事務復原;

2.下遊服務 出錯,觸發分布式事務, 上下遊服務都會事務復原;(某種程度上講,下遊出錯如果隻有一個下遊,其實不需分布式事務,通過錯誤傳遞單個復原也是可以的。但是如果服務調用鍊很長,中遊服務出錯,需要整個鍊上的服務都事務復原,那麼就有必要都使用seata )

SpringCloud 整合分布式事務元件 Seata (八)前言正文

我們開始模拟: 

第一個場景

上遊服務 coredata 使用seata全局事務注解@GlobalTransactional 标記方法,先插入一個Account資料 ;

然後調用下遊服務 usercent 插入一條資料;

然後 上遊服務 coredata繼續執行業務邏輯,繼續插入資料;

接着模拟上遊服務coredata開始報錯(我們通過name長度故意觸發錯誤);

期望結果:  上下遊兩個服務 在目前方法事務下插入的資料都復原!

上遊服務 coredata 方法:

SpringCloud 整合分布式事務元件 Seata (八)前言正文

通過fegin調用下遊服務 usercent 方法:

SpringCloud 整合分布式事務元件 Seata (八)前言正文

下遊服務 usercent 的插入方法:

SpringCloud 整合分布式事務元件 Seata (八)前言正文

開始模拟:

1.先把eureka跑起來:

SpringCloud 整合分布式事務元件 Seata (八)前言正文

暫時就注冊中心自己,沒有别的服務:

SpringCloud 整合分布式事務元件 Seata (八)前言正文

2.把seata server 服務跑起來,注冊到eureka上去:

我是window環境,執行.bat

SpringCloud 整合分布式事務元件 Seata (八)前言正文
SpringCloud 整合分布式事務元件 Seata (八)前言正文

可以看到已經成功注冊到eureka上了:

SpringCloud 整合分布式事務元件 Seata (八)前言正文

3. 把上遊微服務 coredata 和 下遊服務 usercent 都跑起來:

SpringCloud 整合分布式事務元件 Seata (八)前言正文

然後看下seata server上,也可以看到 兩個服務 都成功‘注冊’到了 seata server上了,而且都在同一個事務組 test_tx_group裡面:

SpringCloud 整合分布式事務元件 Seata (八)前言正文

接下來,就是開始調用一下我們模拟的場景代碼就完事了(不過我會加點圖來給大家簡單分析下):

一開始,都是沒有資料的:

SpringCloud 整合分布式事務元件 Seata (八)前言正文

調用上遊服務coredata接口觸發一下整個流程:

SpringCloud 整合分布式事務元件 Seata (八)前言正文
SpringCloud 整合分布式事務元件 Seata (八)前言正文

調用開始:

我們打斷點到 上遊已經插入過一次資料,下遊也插入過一次資料,

SpringCloud 整合分布式事務元件 Seata (八)前言正文

可以看到上遊服務和下遊服務的資料庫裡面的undo_log表出現了 事務記錄,有關目前事務的 branch_id和 所在事務 xid,而且可以看到在兩個服務内的undo_log表中記錄的xid都是一緻的,代表他們都在一個事務中:

SpringCloud 整合分布式事務元件 Seata (八)前言正文
SpringCloud 整合分布式事務元件 Seata (八)前言正文

然後我們繼續往下執行,故意讓上遊報錯:

SpringCloud 整合分布式事務元件 Seata (八)前言正文
SpringCloud 整合分布式事務元件 Seata (八)前言正文

這時候,接口調用完畢結束:

我們看到seata server裡面的資訊,可以看到全局事務 xid為22080結尾,復原成功:

SpringCloud 整合分布式事務元件 Seata (八)前言正文

復原成功後,undo_log表中的記錄會删除掉:

SpringCloud 整合分布式事務元件 Seata (八)前言正文

當然我們兩個服務裡面也是沒有資料的,因為復原了:

SpringCloud 整合分布式事務元件 Seata (八)前言正文

這裡可能有人會想,你查一下是空就能證明是復原了麼? 

這時候我們也可以利用主鍵自增目前值可以看到确實發生了資料復原的場景:

SpringCloud 整合分布式事務元件 Seata (八)前言正文

第一個場景就到此吧。

接下來我們模拟第二個場景:

上遊服務 coredata 使用seata全局事務注解@GlobalTransactional 标記方法,先插入一個Account資料 ;

然後調用下遊服務 usercent 插入一條資料;

然後下遊服務 直接模拟出錯, 這樣觸發事務復原。

期望結果:  上下遊兩個服務 在目前方法事務下插入的資料都復原!

也就是說我們需要對下遊服務的插入方法裡面做手腳,故意抛出錯誤:

SpringCloud 整合分布式事務元件 Seata (八)前言正文
SpringCloud 整合分布式事務元件 Seata (八)前言正文

快速調用一下:

SpringCloud 整合分布式事務元件 Seata (八)前言正文

可以看到下遊出錯了:

SpringCloud 整合分布式事務元件 Seata (八)前言正文

看下我們的seata server怎麼說,已出發分布式事務,復原成功:

SpringCloud 整合分布式事務元件 Seata (八)前言正文

資料庫裡面的資料也是復原了,空的: 

SpringCloud 整合分布式事務元件 Seata (八)前言正文
SpringCloud 整合分布式事務元件 Seata (八)前言正文

好了,該篇springboot cloud使用eureka整合  分布式事務元件  Seata  就到此吧。

ps: 最近比較忙,每篇文章其實都是用一些零碎時間拼湊出來的。不過我會堅持我的文章的初衷,能讓大家跟着實踐,能搞懂,能學會。

我...隻是個散工。

繼續閱讀