第1章:撰寫腳本start-seata.sh并且授權:
| #!/bin/sh docker rm -f seata docker run -d --privileged=true --restart always --name seata --net=host --name seata -p8091:8091 seataio/seata-server:latest |
授權可執行腳本檔案:chmod +x ./start-seata.sh
直接啟動腳本 ./start-seata.sh
查看啟動效果: docker logs -f seata

第2章:搭建SpringCloud專案證明seata分布式事務
2.1【】這里先證明seata基于file檔案的形式

父專案的pom檔案內容:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<packaging>pom</packaging>
<modules>
<module>common</module>
<module>business-demo</module>
<module>order-demo</module>
<module>storage-demo</module>
</modules>
<modelVersion>4.0.0</modelVersion>
<groupId>boss.zkt</groupId>
<artifactId>seata-base-file</artifactId>
<version>0.0.1</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>boss.zkt</groupId>
<artifactId>common</artifactId>
<version>0.0.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
</dependency>
<!-- druid資料源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
<!-- mybatis整合springboot -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>



這種方式的確可以成功實作,但是很多個微服務,復制粘貼代碼很麻煩,主要是改一個,全部都得改,所以搞一個公共的模塊配置,用maven聚合到專案里邊,就簡單多了,
那到底怎么做呢?創建一個common的模塊,
對所有微服務,進行統一注冊nacos,
所以撰寫一個公共的NacosCommonConfig,等會全部微服務參考這個模塊,
package boss.zkt.config.nacos;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.core.env.Environment;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Objects;
/**
* creator:全真教
* date: 2020/4/27
*/
@SpringBootConfiguration
@Slf4j
public class NacosCommonConfig {
@Resource
private NacosDiscoveryProperties nacosDiscoveryProperties;// 這種方式是比較方式,看原始碼出來
@Resource
Environment environment;
@PostConstruct
public void initNacos() {
String appName = environment.getProperty("spring.application.name");// 拿到微服務的名字
String serverAddr = "你的地址:8848";//開發環境用測驗服務器的公網
String[] activeProfiles = environment.getActiveProfiles();
if (activeProfiles.length > 0) {
if ("pro".equals(activeProfiles[0])) {
serverAddr = "172.16.25.162:8848";// 正式環境的私網
} else if ("dev".equals(activeProfiles[0])) {
serverAddr = "你的地址:8848";// 開發環境用測驗服務器的公網
// nacosDiscoveryProperties.setWatchDelay(2000L);// 從nacos獲取服務串列的頻率(2秒一次)
// nacosDiscoveryProperties.setHeartBeatInterval(1);// 給nacos發送心跳的時間間隔
// nacosDiscoveryProperties.setHeartBeatTimeout(3);// nacos多少秒沒有收到這個心跳,就直接把這個微服務洗掉
}else if("test".equals(activeProfiles[0])){
serverAddr = "你的地址:8848";// 開發環境用測驗服務器的公網
}
}
log.warn("#######" + appName + ":配置nacos的環境" + (activeProfiles.length > 0 ? activeProfiles[0] : "無配置") + "地址:" + serverAddr);
try {
InetAddress addr = InetAddress.getLocalHost();
nacosDiscoveryProperties.setPassword("nacos");
nacosDiscoveryProperties.setUsername("nacos");
nacosDiscoveryProperties.setIp(addr.getHostAddress());
log.warn("服務器獲取自身ip地址成功:" + addr.getHostAddress());
} catch (UnknownHostException e) {
log.warn("服務器獲取自身ip地址失敗,將采用自動獲取ip地址");
}
nacosDiscoveryProperties.setPort(Integer.parseInt(Objects.requireNonNull(environment.getProperty("server.port"))));
nacosDiscoveryProperties.setServerAddr(serverAddr);
}
}
如此之外,seata也應該配置相同的組態檔:SeataCommonConfig
package boss.zkt.config.seata;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import io.seata.spring.boot.autoconfigure.properties.SeataProperties;
import io.seata.spring.boot.autoconfigure.properties.SpringCloudAlibabaConfiguration;
import io.seata.spring.boot.autoconfigure.properties.client.ServiceProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* @author:牧牛者 說明:
*/
@SpringBootConfiguration
@Slf4j
public class SeataCommonConfig {
@Resource
private SeataProperties seataProperties;
@Resource
private InitializingBean serviceProperties; // 這里是動態代理生成的,所以只能注入介面,
/**
* 默認 情況 seata會先加載 SpringCloudAlibabaConfiguration 的txServiceGroup 默認就是
* spring.application.name-seata-service-group 顯然是不行的
* 所以這里不讓SpringCloudAlibabaConfiguration 先產生,應該在我們SeataProperties 實體 之后產生
* // 只是為了 SpringCloudAlibabaConfiguration 實體延后產生
*/
@Component
@Primary
@DependsOn(value = {"serviceProperties"})
class MySpringCloudAlibabaConfiguration extends SpringCloudAlibabaConfiguration {
}
@PostConstruct
private void initSeataConfig() {
String txServiceGroup = "my_test_tx_group";// 這里跟你file.conf中的一致
// springCloudAlibabaConfiguration.setTxServiceGroup("my_test_tx_group");
seataProperties.setTxServiceGroup(txServiceGroup);
// 同樣的,這里是動態代理生成的,所以原本的類沒有加入記憶體,所以只能拿到實體,用反射獲取動態代理的類
// 然后用反射獲取 設定 seata服務器地址的方法,注意方法的引數是Map.class
Class<? extends InitializingBean> cls = serviceProperties.getClass();
Map<String, String> grouplist = new HashMap<>();
grouplist.put("default", "你的地址:8091");
try {
Method setGrouplist = cls.getDeclaredMethod("setGrouplist", Map.class);
setGrouplist.invoke(serviceProperties, grouplist);
} catch (Exception e) {
log.error("seata的配置出錯");
}
}
}

后把這個模塊在父級pom中宣告:

然后再把這個模塊引入到其他的微服務:分別是business、order、storage微服務:

其他微服務也一樣,
注意:common模塊里邊不僅僅有我們自己的配置,還有SpringCloud的和Spring cloud For alibaba 還有資料庫鏈接池、mybatis、mysql驅動等,所以common的pom檔案是這個樣子:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>boss.zkt</groupId>
<artifactId>seata-base-file</artifactId>
<version>0.0.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- nacos的注冊發現-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- druid資料源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<!-- mybatis整合springboot -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/io.seata/seata-all -->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
同樣的,在common模塊中創建resources資源檔案,把file.conf和registry.conf 這個兩個檔案放進去,

這樣就不用把這個檔案復制到每個微服務了,
接下來就是改這兩個檔案了,如果你是從官網下載的,那么這兩個檔案,壓根不用改,什么都不用改,直接就可以用,
這里,我把多余的什么基于nacos、zk、eureka等的配置全部刪了,這樣看起來簡潔點:
registry.conf:
registry {
type = "file"
file {
name = "file.conf"
}
}
config {
type = "file"
file {
name = "file.conf"
}
}
file.conf:只需要改一個地方:或者不改:vgroup_mapping.my_test_tx_group = "default"
my_test_tx_group 改成你想要的,但是要注意跟yml中一致,切記哦
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 {
# my_test_tx_group 這個欄位一定要跟yml或者你自己定義的公共配置類里邊的哪個事務分組欄位一樣
vgroup_mapping.my_test_tx_group = "default"
# 這個配置在這里是無法生效的,配了也是白配(這是個坑),請在yml檔案或者自定義配置類里邊去配置
default.grouplist = "39.103.154.143:8091"
enableDegrade = false
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
}
}
好了,接下來準備資料庫和資料表:
三個資料庫,business_db、seata_order_db、seata_storage_db
分別三張表:

undo_log的sql:
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) NOT NULL,
`log_modified` datetime(0) NOT 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;
如果庫存多了,訂單表也不會插入資料,
2.2【】啟動服務:postman測驗
就實作了分布式事務了
以上主要是講解了搭建seata整合springCloud,官放的檔案在我看來有些地方寫的有歧義,再加上版本變來變去,容易混淆,于是搞了這個文章,幫助那些整和seata遇到坑的朋友,
官方說了,如果seata實用單機服務器,那么file模式性能跟高,后面將實用nacos來實作,
總結:要注意的點:
- pom依賴的時候,其實可以根本不需要排除,

2、建議不要用seata-all依賴的方式,seata-spring-boot-starter方式簡
3、file.conf中的default.grouplist 是無效的
4、vgroup_mapping.my_test_tx_group 這里的事務名稱要跟你yml或者自定義配置的公共類中事務名稱一致,
5、SpringCloudAlibabaConfiguration 會先啟動,就默認獲取的事務組名稱很坑,讓他后啟動,

其他的就是maven聚合時候要注意的一些版本號,依賴傳遞,聚合的事情了,我把全部要注意的點都講清楚了,希望你一次搞定,一次實作分布式事務控制,
全真教 包教會
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/357272.html
標籤:其他
