#!/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
父项目的pom文件内容:
org.springframework.boot
spring-boot-starter-parent
2.3.6.RELEASE
pom
common
business-demo
order-demo
storage-demo
4.0.0
boss.zkt
seata-base-file
0.0.1
8
8
org.projectlombok
lombok
1.18.12
boss.zkt
common
0.0.1
mysql
mysql-connector-java
8.0.20
com.alibaba
druid
1.1.22
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.0
com.alibaba.cloud
spring-cloud-alibaba-dependencies
2.2.1.RELEASE
pom
import
org.springframework.cloud
spring-cloud-dependencies
Hoxton.SR9
pom
import
这种方式的确可以成功实现,但是很多个微服务,复制粘贴代码很麻烦,主要是改一个,全部都得改,所以搞一个公共的模块配置,用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 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文件是这个样子:
boss.zkt
seata-base-file
0.0.1
4.0.0
common
8
8
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
org.springframework.cloud
spring-cloud-starter-openfeign
org.springframework.boot
spring-boot-starter-web
mysql
mysql-connector-java
com.alibaba
druid
org.mybatis.spring.boot
mybatis-spring-boot-starter
io.seata
seata-spring-boot-starter
1.4.2
com.alibaba.cloud
spring-cloud-starter-alibaba-seata
io.seata
seata-spring-boot-starter
同样的,在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;
就实现了分布式事务了
以上主要是讲解了搭建seata整合springCloud,官放的文档在我看来有些地方写的有歧义,再加上版本变来变去,容易混淆,于是搞了这个文章,帮助那些整和seata遇到坑的朋友。
官方说了,如果seata实用单机服务器,那么file模式性能跟高。后面将实用nacos来实现。
2、建议不要用seata-all依赖的方式,seata-spring-boot-starter方式简
3、file.conf中的default.grouplist 是无效的
4、vgroup_mapping.my_test_tx_group 这里的事务名称要跟你yml或者自定义配置的公共类中事务名称一致。
5、SpringCloudAlibabaConfiguration 会先启动,就默认获取的事务组名称很坑,让他后启动。
其他的就是maven聚合时候要注意的一些版本号,依赖传递,聚合的事情了。我把全部要注意的点都讲清楚了,希望你一次搞定,一次实现分布式事务控制。
全真教 包教会