本篇文章是从Seata官方样例改造而来的分布式事务,Nacos为服务的注册中心及配置中心,这里我采用的组件的版本为:Nacos 2.1.0、Seata 1.5.2
Seata的官方样例库为:https://github.com/seata/seata-samples
<properties>
<maven.compiler.source>11maven.compiler.source>
<maven.compiler.target>11maven.compiler.target>
<spring-cloud.version>2021.0.4spring-cloud.version>
<cloud.alibaba.version>2021.0.4.0cloud.alibaba.version>
<nacos.version>2.1.0nacos.version>
<cloud.version>3.1.2cloud.version>
<druid.version>1.2.11druid.version>
<dynamic-datasource.version>3.5.1dynamic-datasource.version>
<mybatis-plus.version>3.5.2mybatis-plus.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>${cloud.alibaba.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.alibaba.nacosgroupId>
<artifactId>nacos-clientartifactId>
<version>2.1.1version>
dependency>
dependencies>
dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-bootstrapartifactId>
<version>3.1.4version>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>dynamic-datasource-spring-boot-starterartifactId>
<version>${dynamic-datasource.version}version>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>${mybatis-plus.version}version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>${druid.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-seataartifactId>
<exclusions>
<exclusion>
<groupId>io.seatagroupId>
<artifactId>seata-spring-boot-starterartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>io.seatagroupId>
<artifactId>seata-spring-boot-starterartifactId>
<version>1.4.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
<version>8.0.16version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
<version>1.18.24version>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-loadbalancerartifactId>
<version>${cloud.version}version>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-openfeign-coreartifactId>
<version>${cloud.version}version>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
<version>${cloud.version}version>
dependency>
dependencies>
模块的总体结构如下,这里只列出account-server的部分内容,具体内容可访问官方仓库。
account库的数据库表
CREATE TABLE `account`
(
`id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`user_id` bigint(11) DEFAULT NULL COMMENT '用户id',
`total` decimal(10, 0) DEFAULT NULL COMMENT '总额度',
`used` decimal(10, 0) DEFAULT NULL COMMENT '已用余额',
`residue` decimal(10, 0) DEFAULT '0' COMMENT '剩余可用额度',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
INSERT INTO `account` (`id`, `user_id`, `total`, `used`, `residue`)
VALUES ('1', '1', '100', '0', '100');
每个库还得加上seata的undo_log数据表。undo-log 是 AT 模式中的核心部分 , 他是在 RM 部分完成的 , 在每一个数据库单元处理时均会生成一条 undoLog 数据。
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=17 DEFAULT CHARSET=utf8;
bootstrap.yml文件内容
具体的配置信息配置在Nacos上,所以要在bootstrap.yml上配置上Nacos的信息。
server:
port: 8081
nacos:
service:
address: 127.0.0.1:8848
# spring配置
spring:
application:
name: feign-account-server
profiles:
active: dev
cloud:
nacos:
discovery:
server-addr: ${nacos.service.address}
namespace: wey
config:
#配置中心地址
server-addr: ${nacos.service.address}
namespace: wey
# 配置文件格式
file-extension: yml
# 共享配置
shared-configs:
- feigin-seata-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
Nacos上的配置
Nacos上的feign-account-server-dev.yml 文件的配置内容,order模块跟storage模块的内容类似。
spring:
datasource:
druid:
stat-view-servlet:
enabled: true
loginUsername: admin
loginPassword: 123456
dynamic:
druid:
initial-size: 5
min-idle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
filters: stat,slf4j
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
primary: master
#设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候会抛出异常,不启动则使用默认数据源.
strict: false
#seata1.0之后支持自动代理 这里直接配置true
seata: true
#seata模式使用的at
seata-mode: at
datasource:
# 主库数据源
master:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/feign-seata-account?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
seata:
# 默认关闭,如需启用spring.datasource.dynami.seata需要同时开启
enabled: true
# Seata 应用编号,默认为 ${spring.application.name}
application-id: ${spring.application.name}
# Seata 事务组编号,用于 TC 集群名,这个自定义命名很重要,server,client都要保持一致
tx-service-group: my_test_tx_group
# 关闭自动代理
enable-auto-data-source-proxy: false
# 服务配置项
service:
# 虚拟组和分组的映射
vgroup-mapping:
coolpad-apioutweb-application-group: default
config:
type: nacos
nacos:
serverAddr: 127.0.0.1:8848
group: SEATA_GROUP
namespace: seata
registry:
type: nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
namespace: seata
feigin-seata-dev.yml 配置的是共享的配置信息,可以将公共的配置放在这个文件内。
# mybatis-plus配置
mybatis-plus:
checkConfigLocation: false
configuration:
aggressiveLazyLoading: true
autoMappingBehavior: PARTIAL
autoMappingUnknownColumnBehavior: NONE
defaultEnumTypeHandler: org.apache.ibatis.type.EnumTypeHandler
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapUnderscoreToCamelCase: true
global-config:
banner: true
# 搜索指定包别名
typeAliasesPackage: com.wey.**.entity
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath:mapper/**/*.xml
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 10000
最后集成调用成环,通过@GlobalTransaction注解开户分布式事务。
@Override
@GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class) //此注解开启全局事务
public void create(Order order) {
//本地方法 创建订单
orderDao.create(order);
//远程方法 扣减库存
stockApi.decrease(order.getProductId(),order.getCount());
//远程方法 扣减账户余额 可在accountServiceImpl中模拟异常
accountApi.decrease(order.getUserId(),order.getMoney());
}
在最初的order会创建一个订单,然后扣减库存,然后扣减账户,账户扣减完,会回头修改订单的金额和状态,这样调用就成环了。