3-Alibaba-Seata
(事务测试)学习笔记2020.10.28
前言:
前面, 已经将工程搭建完成, 下面进行项目的分布式事务测试, 看看结果是怎么样?
根据业务流程进行调用: 下订单 - 减库存 - 扣余额
1.0 进行测试
1.1.1 数据库造数据
为了测试方便直接在数据库中的库存与账户表里面造一条数据。
顺便查看
nacos
管理中心是否应用服务都成功注册上去
1.1.2 OrderServiceImpl
具体实现
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private StorageFegin storageFegin;
@Autowired
private AccountFegin accountFegin;
@Transactional
@Override
public int addOrder(Order order) {
order.setCount(3);
order.setAmount(order.getCount()*998);
order.setCommodityCode(String.valueOf(10001));
log.info("全部流程开始");
log.info("创建的订单:{}", JSON.toJSONString(order));
int addResult = orderMapper.addOrder(order);
log.info("创建订单的结果:{}",addResult);
log.info("减库存流程开始");
int updatedStorage = storageFegin.updatedStorage(order.getCommodityCode(), order.getCount());
log.info("减库存流程结果:{}",updatedStorage);
log.info("扣余额流程开始");
int updatedAccount = accountFegin.updatedAccount(order.getUserId(), Double.valueOf(String.valueOf(order.getAmount())).doubleValue());
log.info("扣余额流程结果:{}",updatedAccount);
log.info("全部流程结束");
return addResult;
}
}
1.1.3 先进行测试正常情况的流程
直接调起创建订单
api
接口,下面是结果:
1.1.3 进行异常测试
由于进行了账号进行了一次扣款后, 余额已经无法进行第2次扣款了。
AccountServiceImpl
中的代码
@Autowired
private AccountMapper accountMapper;
@Transactional
@Override
public int updatedAccount(Integer userId, Double amount) {
Account account = accountMapper.findAccountByUserId(String.valueOf(userId));
if (account.getMoney().compareTo(amount) == -1){
throw new RuntimeException("账号金额小于订单金额, 无法扣款!");
}
account.setMoney(account.getMoney()-amount);
return accountMapper.updatedAccount(account);
}
在次进行api下单, 订单由于远程调用到账户微服务, 异常抛到了订单调用, 订单插入会回滚, 我们重点是看
中间的扣库存流程是否回滚
。
从上图可以看出, 分布式架构出现的问题就在于中间流程。
1.1.4 使用Seata
解决分布式事务
官网上的例子上说明, 只需要添加一个注解
@GlobalTransactional
里面的属性有 :
事务超时
timeoutMills
全局事务实例的名称
name
(唯一不重复)回滚的异常类型
rollbackFor
回滚的异常类名称
rollbackForClassName
不回滚的异常类型
noRollbackFor
不回滚的异常类名称
noRollbackForClassName
事务是否需要传播
propagation
(默认就是需要)
1.1.5 OrderServiceImpl
改造
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private StorageFegin storageFegin;
@Autowired
private AccountFegin accountFegin;
//@Transactional 替换成为全局事务 名称自定义, 唯一 回滚所有异常
@GlobalTransactional(name = "orderService-create",rollbackFor = Exception.class)
@Override
public int addOrder(Order order) {
order.setCount(3);
order.setAmount(order.getCount()*998);
order.setCommodityCode(String.valueOf(10001));
log.info("全部流程开始");
log.info("创建的订单:{}", JSON.toJSONString(order));
int addResult = orderMapper.addOrder(order);
log.info("创建订单的结果:{}",addResult);
log.info("减库存流程开始");
int updatedStorage = storageFegin.updatedStorage(order.getCommodityCode(), order.getCount());
log.info("减库存流程结果:{}",updatedStorage);
log.info("扣余额流程开始");
int updatedAccount = accountFegin.updatedAccount(order.getUserId(),
Double.valueOf(String.valueOf(order.getAmount())));
log.info("扣余额流程结果:{}",updatedAccount);
log.info("全部流程结束");
return addResult;
}
}
1.1.6 重启订单微服务在次进行测试
调用后报了以下的错误:
io.seata.core.exception.TmTransactionException: TransactionException[begin global request failed. xid=null, msg=Data truncation: Data too long for column 'transaction_service_group' at row 1]
说的是
Seata
事务组名称过长了, 将订单微服application.yml
中的spring.cloud.alibaba.seata.tx-service-group=order-service-group
修改短, 同时将file.conf
中的file.conf文件中 service中vgroup_mapping.xxx="default"
也修改为一样, 将其他微服的自行修改。下面测试结果:
到此, 分布式事务就实现了。
项目Demo (用的seata
版本是1.3.0)
1