本文主要选择nacos作为注册中心使用,下面记录一下操作的步骤。主要是网上有一些坑,并且网上有些文章不合理
1.下载seata
我下载的seata的版本是1.4.2
下载地址:https://github.com/seata/seata/releases
下载完成之后解压就行(我下载的是压缩包)
2.seata配置
(1).配置config/registry.conf
registry.config文件主要是seata注册中心地址的配置,里面包括了各种注册中心的配置样例,我们这里选择了nacos为注册中心,所以主需要保留nacos的配置,其他的都删掉。我配置的文件如下:
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos"
nacos {
application = "seata-server" #seata注册到nacos上面的应用名
serverAddr = "127.0.0.1:8848" #注册中心的地址
group = "SEATA_GROUP" #分组
namespace = "abc34810-6e99-41dd-a5c3-c9f09fcced39" #命名空间,强烈建议新建一个namespage单独用于seata,填写命名空间id
cluster = "default" #集群名
username = "" #nacos的用户名
password = "" #nacos密码
}
}
config {
# file、nacos 、apollo、zk、consul、etcd3
type = "nacos"
nacos {
serverAddr = "127.0.0.1:8848"
namespace = "abc34810-6e99-41dd-a5c3-c9f09fcced39"
group = "SEATA_GROUP"
username = ""
password = ""
dataId = "seataServer.properties"
}
}
上面主要是注册中心的配置,这一提醒一点,最好是新建一个单独的namespace专门用于做seata分布式事务使用,不然后面配置多了很难查找,并且很难维护!
(2).配置file.config
file.config主要用于配置存储事务日记,可以是file,redis,db,我选择的是db
## transaction log store, only used in seata-server
store {
## store mode: file、db、redis
mode = "db"
## rsa decryption public key
##publicKey = ""
## file store property
## database store property
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
datasource = "druid"
## mysql/oracle/postgresql/h2/oceanbase etc.
dbType = "mysql"
driverClassName = "com.mysql.jdbc.Driver"
## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param
url = "jdbc:mysql://127.0.0.1:3306/seata"
user = "root"
password = "123456"
minConn = 5
maxConn = 100
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
}
上面是我的配置,大家可以按照自己的需求配置
(3).运行脚本
新版本的seata里面没有nacos-conf.sh和config.txt两个文件,可以去github上面下载,下载地址如下
nacos-config.sh:https://github.com/seata/seata/blob/develop/script/config-center/nacos/nacos-config.sh
config.txt:https://github.com/seata/seata/blob/develop/script/config-center/config.txt
运行nacos-conf.sh,其实就是将config.txt的配置信息上传到nacos上面,后面seata直接从nacos上面去数据了。如果你是windows操作系统,直接运行不了nacos-conf.sh,可以通过git-bash运行
运行命令之前可以修改conf.txt的内容,也可以上传到nacos后,在nacos的管理台修改
修改的内容如下:
#1.我这里配置了我的db的连接信息
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=123456
#2.修改分布式事务的分组
service.vgroupMapping.my_test_tx_group=default
#我增加了下面两个
service.vgroupMapping.device=default #这里是固定写法将device修改成你的应用里面的application.yml里面的seata.tx-service-group名称
service.vgroupMapping.task=default #
例如我device微服务的配置
运行命令
sh nacos-config.sh -h localhost -p 8848 -g SEATA_GROUP -u nacos -w nacos -t abc34810-6e99-41dd-a5c3-c9f09fcced39
#-h nacos的地址
#-p nacos服务的端口
#-g group
#-u nacos用户名
#-w nacos密码
#-t 租户,这里就是上面创建的namespace id,这里非常重要,一定要小心,不然后面启动应用会一直报警告信息
运行完成之后:
这里显示失败了几个,没关系。上传到nacos后的样子
(4)创建数据库和表
在自己微服务数据库里面创建undo_log表,sql在源码里面有
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=5 DEFAULT CHARSET=utf8
在配置的seata库里面创建下面三张表
-- -------------------------------- 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;
到这里SEATA的配置基本上配置完了,我们直接运行SEATA,直接运行点击bin/seata-server.bat,若是linux版本,运行sh脚本即可。到这里SEATA服务的配置基本上结束了,我们下面配置自己的微服务应用了
(5)配置微服务
我的微服务是device和task,我的springcloud的版本和springcloudalibaba的版本如下
下面是dependencyManagement的配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<spring-cloud-starter-seata.version>2.2.0.RELEASE</spring-cloud-starter-seata.version>
<seata.springboot.starter.version>1.4.2</seata.springboot.starter.version>
<seata-all.version>1.4.2</seata-all.version>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<version>${spring-cloud-starter-seata.version}</version>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>${seata-all.version}</version>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>${seata.springboot.starter.version}</version>
</dependency>
下面是我每个微服务的pom关于seata的配置
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<!-- 排除依赖 指定版本和服务器端一致 -->
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
</exclusion>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</dependency>
这里我期望在yml中配置seata的配置,可以动态的适配环境的变化(测试环境、预发布环境、生产环境),网上很多帖子描述将SEATA文件夹里面的registry.conf复制到微服务的resources文件夹下面,我觉得不合理,这样每次不同的环境都需要修改这个文件
配置依赖之后,就配置bootstrap.yml文件了,可以根据不同的环境配置不同的bootstrap.yml
seata:
application-id: device
enabled: true
tx-service-group: device #和上面的nacos的上面的配置一致
enable-auto-data-source-proxy: true
config:
type: nacos
nacos:
namespace: abc34810-6e99-41dd-a5c3-c9f09fcced39 #配置上面的namespace
serverAddr: http://10.10.29.155:8848 #nacos地址
group: SEATA_GROUP
userName: nacos
password: nacos
registry:
type: nacos
nacos:
application: seata-server
serverAddr: http://10.10.29.155:8848
group: SEATA_GROUP
namespace: abc34810-6e99-41dd-a5c3-c9f09fcced39
userName: nacos
password: nacos
cluster: default
在其他的微服务里面类似配置
(6)配置动态数据源代理
SEATA需要创建动态数据源代理,用于创建分支事务id,我的配置如下
@Slf4j
@Configuration
public class DbConfig {
@ConfigurationProperties(prefix = "spring.datasource.druid")
@Bean
public DataSource druidDataSource(){
DruidDataSource dataSource = new DruidDataSource();
log.info("create druid dataSource ......");
return dataSource;
}
@Bean
@Primary
public DataSource dataSource (){
log.info("create seata dataSourceProxy");
return new DataSourceProxy(druidDataSource());
}
}
最后还需要springboot不能自动装配数据源,所以我在启动类那里exclude掉了,如下:
/**
* @Author tyler.hong
* @Date 2021/7/29 14:45
* @Version 1.0
* @Description
*/
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})//这里排除掉
@MapperScan({"com.xxx.xxx.device.mapper*"})
@EnableTransactionManagement
@EnableFeignClients(basePackages = {"com.xxx.xxx.api.task"})
public class DeviceApp {
public static void main(String[] args) {
SpringApplication.run(DeviceApp.class,args);
}
}
好了到这里基本上所有的配置就结束了,下面就是测试代码了,很简单,我用的是AT模式
@GetMapping("/testTransaction")
//@GlobalTransactional 加上这个注解就行了
public R testTransaction(){
Test test = new Test();
test.setAge(300)
.setId(333)
.setName("tyler2333");
testService.insert(test);
//模拟一场
int a = 1/0;
testFeignService.testTransaction();
return success();
}