SpringCloud+Nacos+Mybatis Plus+OpenFeign+Seata整合分布式脚手架
本篇文章继续在: SpringCloud(Springboot)+Mybatis Plus . 基础上完善
本文代码说明:在底部源码里面seata里面是seata 安装包, 相关配置文件都有。
准备seata环境
-- 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(128),
`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;
- 在每个需要分布式事务的服务里面增加undo_log表:
-- for AT mode you must to init this sql for you business database. the seata server not need it.
CREATE TABLE IF NOT EXISTS `undo_log`
(
`branch_id` BIGINT NOT NULL COMMENT 'branch transaction id',
`xid` VARCHAR(128) NOT NULL COMMENT 'global transaction id',
`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',
`log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',
`log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',
`log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime',
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
- 将script/config-center下config.txt 复制到seata目录下, 并修改其中配置, 如下:
配置说明:
service.vgroupMapping.nacos-server-group=default
service.vgroupMapping.nacos-config-group=default
要与每个服务里面配置相同
store.lock.mode=db
store.session.mode=db 则是指seata 采用db做持久化
数据库配置
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=root
- 复制script/config-center/nacos下的两个文件到seat/conf/下并修改seat/conf/下file.conf, registry.conf
file.conf:
## mode选择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
file {
## store location dir
dir = "sessionStore"
# branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
maxBranchSessionSize = 16384
# globe session size , if exceeded throws exceptions
maxGlobalSessionSize = 512
# file buffer size , if exceeded allocate new buffer
fileWriteBufferCacheSize = 16384
# when recover batch read size
sessionReloadReadSize = 100
# async, sync
flushDiskMode = async
}
## 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?rewriteBatchedStatements=true"
user = "root"
password = "root"
minConn = 5
maxConn = 100
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
## redis store property
redis {
## redis mode: single、sentinel
mode = "single"
## single mode property
single {
host = "127.0.0.1"
port = "6379"
}
## sentinel mode property
sentinel {
masterName = ""
## such as "10.28.235.65:26379,10.28.235.65:26380,10.28.235.65:26381"
sentinelHosts = ""
}
password = ""
database = "0"
minConn = 1
maxConn = 10
maxTotal = 100
queryLimit = 100
}
}
registry.conf配置:
## registry , config都选择nacos, 同时如下配置nacos信息
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos"
nacos {
application = "seata-server"
serverAddr = "127.0.0.1:8848"
group = "SEATA_GROUP"
namespace = "2ef5294b-bfda-41ee-bb17-876a8d28dcdf"
cluster = "default"
username = "dev"
password = "dev"
}
eureka {
serviceUrl = "http://localhost:8761/eureka"
application = "default"
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"
aclToken = ""
}
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 = "nacos"
nacos {
serverAddr = "127.0.0.1:8848"
group = "SEATA_GROUP"
namespace = "2ef5294b-bfda-41ee-bb17-876a8d28dcdf"
cluster = "default"
username = "dev"
password = "dev"
}
consul {
serverAddr = "127.0.0.1:8500"
aclToken = ""
}
apollo {
appId = "seata-server"
## apolloConfigService will cover apolloMeta
apolloMeta = "http://192.168.1.204:8801"
apolloConfigService = "http://192.168.1.204:8080"
namespace = "application"
apolloAccesskeySecret = ""
cluster = "seata"
}
zk {
serverAddr = "127.0.0.1:2181"
sessionTimeout = 6000
connectTimeout = 2000
username = ""
password = ""
nodePath = "/seata/seata.properties"
}
etcd3 {
serverAddr = "http://localhost:2379"
}
file {
name = "file.conf"
}
}
- 使用命令将seata 配置推送到nacos
sh nacos-config.sh -h ip -p port -g 分组 -t 空间id -u 用户 -w 密码
ru: sh nacos-config.sh -h 127.0.0.1 -p 8848 -g SEATATA_GROUP -t 2ef5294b-bfda-41ee-bb17-876a8d28dcdf -u dev -w dev
失败的几项不用管,是redis的配置, 暂时用不到
- 检查nacos 配置列表:
- 启动seata 服务:
sh seata-server.sh -p 8091 -m db 或者点击seata-server.bat 启动
nacos服务列表:
在nacos-config, nacos-server里配置seata
- 首先在两个服务里面添加seata jar包
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</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>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
</exclusion>
</exclusions>
</dependency>
- 在nacos-config配置文件配置如下文件:
seata:
tx-service-group: nacos-config-group #与配置文件里面config.txt seata.
tx-service-group 必须一样
enabled: true
# 是否开启数据源自动代理 如果不开启设置为false
enable-auto-data-source-proxy: true
application-id: ${spring.application.name}
service:
vgroup-mapping:
nacos-config-group: default #key与上面的tx-service-group的值对应
registry:
type: nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
namespace: 2ef5294b-bfda-41ee-bb17-876a8d28dcdf
group: SEATA_GROUP
userName: dev
password: dev
config:
nacos:
namespace: 2ef5294b-bfda-41ee-bb17-876a8d28dcdf
serverAddr: 127.0.0.1:8848
group: SEATA_GROUP
userName: dev
password: dev
- 在nacos-server配置文件配置如下文件:
seata:
tx-service-group: nacos-server-group #与配置文件里面config.txt seata.
tx-service-group 必须一样
enabled: true
# 是否开启数据源自动代理 如果不开启设置为false
enable-auto-data-source-proxy: true
application-id: ${spring.application.name}
service:
vgroup-mapping:
nacos-server-group: default #key与上面的tx-service-group的值对应
registry:
type: nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
namespace: 2ef5294b-bfda-41ee-bb17-876a8d28dcdf
group: SEATA_GROUP
userName: dev
password: dev
config:
nacos:
namespace: 2ef5294b-bfda-41ee-bb17-876a8d28dcdf
serverAddr: 127.0.0.1:8848
group: SEATA_GROUP
userName: dev
password: dev
- 启动类上添加@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) 因为seata 分布式事务需要seata的代理数据源
- 涉及的方法上添加本地事务, 全局事务要观察本地事务的执行状态(修正: 本地事务与seata无关, seata是有Tm管理全局事务跟分支事务, 所以添加不添加Transactional 与seata 分布式事务无关)
package com.chen.scaffolding.nacosconfig.service.impl;
import com.chen.scaffolding.nacosconfig.entity.Authority;
import com.chen.scaffolding.nacosconfig.mapper.AuthorityMapper;
import com.chen.scaffolding.nacosconfig.service.AuthorityService;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 权限
* @author :master
*/
@Service
public class AuthorityServiceImpl implements AuthorityService {
@Autowired
private AuthorityMapper authorityMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public void addAuthority(Authority authority) {
authorityMapper.addAuthority(authority);
}
@Override
public void updateAuthority(Authority authority) {
authorityMapper.updateAuthority(authority);
}
}
- 调用方设置开启全局事务@GlobalTransactional(rollbackFor = Exception.class)
package com.chen.scaffolding.nacosserver.service.impl;
import com.chen.scaffolding.nacosconfig.entity.Authority;
import com.chen.scaffolding.nacosserver.entity.UserEntity;
import com.chen.scaffolding.nacosserver.mapper.UserMapper;
import com.chen.scaffolding.nacosserver.service.UserService;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 用户类
* @author :master
*/
@Service
public class UserServiceImpl implements UserService {
@Autowired
private OpenFeignAuthorityService authorityService;
@Autowired
private UserMapper userMapper;
@Override
@GlobalTransactional(rollbackFor = Exception.class)
@Transactional(rollbackFor = Exception.class)
public void addUser(UserEntity userEntity) {
// 第一步
userMapper.addUser(userEntity);
// 第二步
Authority authority = new Authority();
authority.setAuthId("1");
authority.setAuthName("增加权限");
authority.setUserId("1");
authorityService.addAuthority(authority);
}
@Override
public void updateUser(UserEntity userEntity) {
userMapper.updateUser(userEntity);
}
}