Seata 分布式事务+Nacos 注册配置中心(spring-cloud-alibaba-dependencies:2.2.5.RELEASE)
Seata client采用AT 事务模式
Seata server侧采用数据库模式
预备工作
确保后台已经启动 Nacos 服务。如果您尚且不熟悉 Nacos 的基本使用的话,可先行参考 Nacos 快速入门。建议使用 Nacos 1.2.0 及以上的版本。
获取server服务
gitee: https://gitee.com/seata-io/seata/releases
github: https://github.com/seata/seata/releases
获取配置文件及数据库脚本
gitee: https://gitee.com/seata-io/seata/tree/develop/script
github: https://github.com/seata/seata/tree/develop/script
假设server服务解压缩地址:/Users/user/seata
配置文件及数据库脚本保存地址: /Users/user/script/
- 执行数据库脚本
在业务数据库中添加undo_log表,sql脚本在/Users/user/script//client/at/db/mysql.sql
新建seata数据库,sql脚本在/Users/user/script//server/db/mysql.sql
修改server配置文件
2.1 修改/Users/user/seata/conf/file.conf
2.2 修改/Users/user/seata/registry.conf
2.3 修改下载的配置文件/Users/user/script/conf(这个可以最后在nacos中修改)向Nacos添加配置文件
3.1 打开Nacos控制台,建议新建一个命名空间,因为这个配置文件太多太乱
3.2 找到shell脚本,位置/Users/user/script/config-center/nacos/nacos-config.sh
3.3 打开命令工具执行sh ${SEATAPATH} -h localhost -p 8848 -g SEATA_GROUP -t 5a3c7d6c-f497-4d68-a71a-2e5e3340b3ca -u username -w password SEATAPATH: 你的配置文件位置,上面的/Users/user/script/config-center/nacos/nacos-config.sh -h: nacos服务ip. -p: nacos服务端口号. -g: 想要的分组信息. -t: 第一步新建的命名空间. -u: nacos登录名. -w: nacos登录密码
注意:
如果执行shell后显示
表示config文件查找错误想要手动指定
编辑模式打开nacos-config.sh 第97行 将( d i r n a m e " (dirname "(dirname"PWD")/config.txt 替换为config.txt文件的绝对路径后再执行shell.效果图:
启动Seata server服务
在/Users/user/seata下,创建logs存放日志文件.执行/Users/user/seata/bin/seata-server.sh脚本
nacos服务注册信息nacos上的application.yml文件添加seata信息
config与registry中的信息,是在2.2中设置的,config信息导入到哪里是在执行shell决定的
# Seata 分布式事务
seata:
enabled: true
#seata注册的服务名称
application-id: seata-service
#此处配置自定义的seata事务分组名称
tx-service-group: my_test_tx_group
#开启数据库代理
enable-auto-data-source-proxy: true
config:
type: nacos
nacos:
#nacos地址
server-addr: 127.0.0.1:8848
#配置文件的命名空间
namespace: d4c88b0f-8b0f-8b0f-8b0f-4332b0116b71
#配置文件的分组名
group: SEATA_GROUP
username: nacos
password: nacos
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
#注册服务的命名空间
namespace: d4c88b0f-8b0f-8b0f-8b0f-4332b0116b71
#注册服务的名称
application: seata-server
#注册服务的分组名
group: sds-cloud-01
username: nacos
password: nacos
- 修改pom文件
<!--注意:seata-spring-boot-starter包不具备xid传递功能-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
- 修改代码(模拟下单:销库存-下单-扣款)
//demo.java
@GlobalTransactional//开启全局事务
public ReturnModel placeOrder(int userId, int productId, int num) {
System.out.println("xid======" + RootContext.getXID());
//判断库存 并消减库存
ReturnModel model = productApi.deduct(productId, num);
if (!model.success()) {
throw new RuntimeException(model.toJson());
}
//创建订单
model = orderApi.create(userId, productId, Integer.valueOf(model.getData().toString()), num);
if (!model.success()) {
throw new RuntimeException(model.toJson());
}
return model;
}
//product.java
public ReturnModel deduct(@RequestParam("productId") int productId,
@RequestParam("num") int num) {
System.out.println("xid======" + RootContext.getXID());
Product product = getById(productId);
if (product == null) {
return ReturnModel.create(ExceptionEnum.UNKNOWN_EXCEPTION).msg("产品已下架");
}
if (product.getNum() < num) {
return ReturnModel.create(ExceptionEnum.UNKNOWN_EXCEPTION).msg("库存不足");
}
int price = product.getPrice();
num = product.getNum() - num;
boolean update = productService.update(new LambdaUpdateWrapper<Product>().set(Product::getNum, num).eq(Product::getId, productId));
if (update) {
return ReturnModel.create().data(price);
}
return ReturnModel.create(ExceptionEnum.UNKNOWN_EXCEPTION).msg("库存失败");
}
//order.java
public ReturnModel create(int userId, int productId, int price, int num) {
System.out.println("xid======" + RootContext.getXID());
//创建订单
Order order = new Order();
order.setNum(num);
order.setProductId(productId);
order.setUserId(userId);
order.setPrice(price * num);
boolean save = save(order);
if (!save) {
return ReturnModel.create(ExceptionEnum.UNKNOWN_EXCEPTION).msg("订单创建失败");
}
//扣款
return userApi.debit(userId, order.getPrice());
}
//user.java
public ReturnModel debit(@RequestParam("userId") int userId,
@RequestParam("price") int price) {
System.out.println("xid======" + RootContext.getXID());
User user = getById(userId);
if (user == null) {
return ReturnModel.create(ExceptionEnum.UNKNOWN_EXCEPTION).msg("用户不存在");
}
if (user.getIntegral() < price) {
return ReturnModel.create(ExceptionEnum.UNKNOWN_EXCEPTION).msg("用户余额不足");
}
user.setIntegral(user.getIntegral() - price);
boolean b = userService.updateById(user);
if (b) {
return ReturnModel.create();
}
return ReturnModel.create(ExceptionEnum.UNKNOWN_EXCEPTION).msg("扣款失败");
}
- 请求链路图
- Seata server 执行日志