需求背景:
模拟下单场景:首先去在自己的本地创建一条下单记录,同时,还要去调用库存服务,执行减库存操作。
这里演示一个客户下订单的流程服务来为小伙伴们进一步了解一下分布式事务到底如何使用?
首先,我会把系统模块中,开发订单需求;然后,再创建一个库存的子模块,最后,客户端发起订单请求,系统模块接收到请求后,在本地数据库中创建一个。
文章目录
- 一、数据库部分
- 1. 订单表创建
- 2. 库存数据库创建
- 3. 库存表结构初始化
- 4. 添加依赖
- 二、订单微服务代码部分
- 2.1. 创建实体类
- 2.2. 创建接口类
- 2.3. 调整控制层逻辑
- 2.4. 修改配置文件
- 三、库存微服务代码部分
- 3.1. 创建实体类
- 3.2. 接口库存Dao
- 3.3. 容错代码
- 3.4. 控制层逻辑调整
- 3.5. 配置文件修改
- 3.6. 初始化库存
- 3.7. 容错代码简述
- 四、测试验证
- 4.1. 启动服务
- 4.2. 发起第一轮请求
- 4.3. 抛出异常
- 4.4. 异常信息监控
- 4.5. 流程梳理
- 4.6. 数据库验证
- 4.7. 发起第二轮请求
- 4.8. 发起第三轮请求
- 4.9. 数据库数据验证
- 4.10. 发起第四轮请求
- 4.11. 数据库验证
一、数据库部分
1. 订单表创建
在系统模块连接的,ry-cloud数据库中创建一张订单表 和 AT模式seata需要用到的undolog表,其中undo_log表,在前面已经创建过了
create table order_tb
(
id int auto_increment
primary key,
user_id int not null,
product_id int not null
);
CREATE TABLE `undo_log` (
`branch_id` bigint(20) NOT NULL COMMENT 'branch transaction id',
`xid` varchar(100) 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 DEFAULT CHARSET=utf8mb4 COMMENT='AT transaction mode undo table';
2. 库存数据库创建
创建ry-stock
数据库
3. 库存表结构初始化
```bash
create table stock
(
id int auto_increment
primary key,
count int not null,
product_id int not null
);
CREATE TABLE `undo_log` (
`branch_id` bigint(20) NOT NULL COMMENT 'branch transaction id',
`xid` varchar(100) 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 DEFAULT CHARSET=utf8mb4 COMMENT='AT transaction mode undo table';
4. 添加依赖
系统模块
<!--Lombok引入-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
二、订单微服务代码部分
我们对现在的order-serv订单服务基础上调整
2.1. 创建实体类
2.2. 创建接口类
2.3. 调整控制层逻辑
2.4. 修改配置文件
三、库存微服务代码部分
3.1. 创建实体类
3.2. 接口库存Dao
3.3. 容错代码
3.4. 控制层逻辑调整
3.5. 配置文件修改
3.6. 初始化库存
3.7. 容错代码简述
四、测试验证
4.1. 启动服务
4.2. 发起第一轮请求
4.3. 抛出异常
4.4. 异常信息监控
4.5. 流程梳理
4.6. 数据库验证
4.7. 发起第二轮请求
4.8. 发起第三轮请求
4.9. 数据库数据验证
4.10. 发起第四轮请求
4.11. 数据库验证
改之前配置
# spring配置
spring:
redis:
host: localhost
port: 6379
password: 123456
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 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
filters: stat,slf4j
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
datasource:
# 主库数据源
master:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ry-cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: 123456
# 从库数据源
# slave:
# username:
# password:
# url:
# driver-class-name:
# seata: true # 开启seata代理,开启后默认每个数据源都代理,如果某个不需要代理可单独关闭
# seata配置
seata:
# 默认关闭,如需启用spring.datasource.dynami.seata需要同时开启
enabled: false
# Seata 应用编号,默认为 ${spring.application.name}
application-id: ${spring.application.name}
# Seata 事务组编号,用于 TC 集群名
tx-service-group: ${spring.application.name}-group
# 关闭自动代理
enable-auto-data-source-proxy: false
# 服务配置项
service:
# 虚拟组和分组的映射
vgroup-mapping:
ruoyi-system-group: default
config:
type: nacos
nacos:
serverAddr: 127.0.0.1:8848
group: SEATA_GROUP
namespace:
registry:
type: nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
namespace:
# mybatis配置
mybatis:
# 搜索指定包别名
typeAliasesPackage: com.ruoyi.system
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath:mapper/**/*.xml
# swagger配置
调整之后配置
# spring配置
spring:
redis:
host: localhost
port: 6379
password: 123456
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 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
filters: stat,slf4j
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
datasource:
# 主库数据源
master:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ry-cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: 123456
# 从库数据源
# slave:
# username:
# password:
# url:
# driver-class-name:
seata: true # 开启seata代理,开启后默认每个数据源都代理,如果某个不需要代理可单独关闭
# seata配置
seata:
# 默认关闭,如需启用spring.datasource.dynami.seata需要同时开启
enabled: true
# Seata 应用编号,默认为 ${spring.application.name}
application-id: ${spring.application.name}
# Seata 事务组编号,用于 TC 集群名
tx-service-group: order-service
# 关闭自动代理
enable-auto-data-source-proxy: false
# 服务配置项
service:
# 虚拟组和分组的映射
vgroup-mapping:
ruoyi-system-group: default
config:
type: nacos
nacos:
serverAddr: 127.0.0.1:8848
group: SEATA_GROUP
namespace: public
userName: "nacos"
password: "nacos"
registry:
type: nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
namespace: public
userName: "nacos"
password: "nacos"
# mybatis配置
mybatis:
# 搜索指定包别名
typeAliasesPackage: com.ruoyi.system
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath:mapper/**/*.xml
# swagger配置