SpringCloud中使用Seata实现分布式事务
Hello,兄弟们好,我是Feri,最近整理了最新的基于Seata-Server2.0实现分布式事务的demo,希望对你有所帮助,有任何问题,可以随时沟通交流,在成为技术大牛的路上,我们一路前行!
Apache Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务
我是使用的Spring Cloud Aliabab实现的微服务,在这里使用的Seata实现的分布式事务
直接使用的官网提供的案例
用户购买商品的业务逻辑。整个业务逻辑由 3 个微服务提供支持:
- 仓储服务:对给定的商品扣除仓储数量。
- 订单服务:根据采购需求创建订单。
- 帐户服务:从用户帐户中扣除余额。
本篇对应的源代码,请看文章末尾有链接哈
实现步骤,搞起来!
1.数据库设计
create database db_seatastudy char set 'utf8mb4';
use db_seatastudy;
DROP TABLE IF EXISTS `storage_tbl`;
CREATE TABLE `storage_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`commodity_code` varchar(255) DEFAULT NULL,
`count` int(11) DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY (`commodity_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `order_tbl`;
CREATE TABLE `order_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) DEFAULT NULL,
`commodity_code` varchar(255) DEFAULT NULL,
`count` int(11) DEFAULT 0,
`money` int(11) DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `account_tbl`;
CREATE TABLE `account_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) DEFAULT NULL,
`money` int(11) DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2.创建微服务项目
采用Maven的父子工程实现
根项目:SeataStudy
子项目:
ss-common 公共项目模块
ss-server 服务模块,内部包括3个服务(分别为账户服务、库存服务、订单服务)
内部子项目:
ss-account:账户服务
ss-storage:库存服务
ss-order:订单服务
ss-consumer 对外的服务消费,内部包括1个服务(订单接口Api)
内部子项目:
ss-orderapi
项目结构截图:
3.实现账户服务
实现账户的新增、修改、查询的接口开发
核心代码:
@RestController
@RequestMapping("/server/account/")
public class AccountTblController{
/**
* 服务对象
*/
@Resource
private AccountTblService service;
@PostMapping("add")
public R<String> add(@RequestBody AccountAdd add){
return service.save(new AccountTbl(add.getUid(),add.getMoney()))?R.ok(""):R.fail("");
}
@GetMapping("all")
public R all(){
return R.ok(service.list());
}
//修改余额
@PostMapping("update")
public Integer updateMoney(@RequestBody AccountAdd update){
return service.updteMoney(update.getUid(),update.getMoney());
}
}
4.实现库存服务
实现库存的新增、修改、查询等功能接口
核心代码:
@RestController
@RequestMapping("/server/storage/")
public class StorageTblController{
/**
* 服务对象
*/
@Resource
private StorageTblService service;
@PostMapping("add")
public R add(@RequestBody StorageAdd add){
return service.save(new StorageTbl(add.getCode(),add.getNum()))?R.ok(""):R.fail("");
}
@GetMapping("all")
public R all(){
return R.ok(service.list());
}
@PostMapping("update")
public Integer update(@RequestBody StorageAdd update){
return service.updateCount(update);
}
}
5.实现订单服务
实现订单的新增、查询等操作接口
核心代码:
@Service("orderTblService")
public class OrderTblServiceImpl implements OrderTblService {
@Resource
private OrderTblDao dao;
@Resource
private AccountService accountService;
@Resource
private StorageService storageService;
@Override
public R<String> add(OrderAdd add) {
//下单 生成订单 扣款-账户 修改库存-库存
//1.生成订单
OrderTbl order=new OrderTbl();
BeanUtils.copyProperties(add,order);
dao.insert(order);
//2.账户 扣款 调用账户服务
accountService.updateMoney(new AccountAdd(add.getUserId(),-add.getMoney()));
//3.库存 更改库存
storageService.update(new StorageAdd(add.getCommodityCode(),-add.getCount()));
return R.ok("下单成功");
}
@Override
public R<OrderTbl> queryAll(Integer uid) {
LambdaQueryWrapper<OrderTbl> wrapper=new LambdaQueryWrapper<>();
if(uid!=null && uid>0){
wrapper.eq(OrderTbl::getUserId,uid);
}
return R.ok(dao.selectList(wrapper));
}
}
6.安装和启动Seata-Server
因为Seata需要Seata-server的管理,所以还需要安装一下Seata-server
第一步:下载seata-server
官网提供的Seata-server下载地址,如果下载不了,可以使用我网盘里面的
网盘地址-最新的Seata-Server2.0,点击下载
启动Seata-server
在bin/seata-server.bat 双击启动即可
浏览器访问:http://localhost:7091/
会让输入账号和密码,默认的账号:seata,默认的密码:seata
7.项目中使用Seata
需要在项目中使用Seata实现分布式事务,保证下单接口操作的完整性
第一步:依赖jar
在ss-server项目中实现依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
第二步:在订单服务的下单方法上开启分布式事务
在ss-order的service.impl的add方法上使用注解
@GlobalTransactional
@Override
public R<String> add(OrderAdd add) {
//下单 生成订单 扣款-账户 修改库存-库存
//1.生成订单
OrderTbl order=new OrderTbl();
BeanUtils.copyProperties(add,order);
dao.insert(order);
//2.账户 扣款 调用账户服务
accountService.updateMoney(new AccountAdd(add.getUserId(),-add.getMoney()));
//3.库存 更改库存
storageService.update(new StorageAdd(add.getCommodityCode(),-add.getCount()));
return R.ok("下单成功");
}
8.测试下单,观察分布式事务的效果
观察,如果某个服务出问题,那么其他会不会回滚
如果会,那么就说明分布式事务生效
为了演示事务,最好模拟一下出错,比如我这里:
@Override
public Integer updateCount(StorageAdd update) {
System.err.println(1/0);//模拟出错
return getBaseMapper().updateCount(update.getNum(),update.getCode());
}
好了,就到这了,你get到了吗?
点击,查看对应的源代码