Seate 处理分布式事务。
微服务模块,连接多个数据库,多个数据源,而数据库之间的数据一致性需要被保证。
使用步骤
1,搭建订单模块:order
2,库存存储模块:storage
3,搭建用户模块:account
案例:买手机,模拟全局事务,超时回滚
如果不加全局事务,如果调用失败,由于fengn的重试机制,还可能余额被重复扣减
//只需要在业务类的方法上加上该注解,name值自定义唯一即可。
@GlobalTransactional(name = "dkf-create-order", rollbackFor = Exception.class)
public void create(Order order) {
log.info("--------》 开始创建订单");
orderDao.create(order);
log.info("--------》 订单微服务开始调用库存,做扣减---Count-");
storageService.decrease(order.getProductId(), order.getCount());
log.info("--------》 订单微服务开始调用库存,库存扣减完成!!");
log.info("--------》 订单微服务开始调用账户,账户扣减---money-");
accountService.decrease(order.getUserId(),order.getMoney());
log.info("--------》 订单微服务开始调用账户,账户扣减完成!!");
//修改订单状态,从0到1
log.info("--------》 订单微服务修改订单状态,start");
orderDao.update(order.getUserId(),1);
log.info("--------》 订单微服务修改订单状态,end");
log.info("--订单结束--");
}
注意:
1,rollbackFor = Exception.class表示哪些异常碰到了一定要进行事务回滚,我们这里设置的是Exception.class表明,只要发生异常,就要实现事物的回滚
2,name = "dkf-create-order"表示全局事务的名称,要保证其唯一性
如上述代码所示
0,在跨模块调用业务的方法上加上@GlobalTransactional注解
1,当我们在订单模块order点击下订单的时候,会进行创建订单操作
log.info("--------》 开始创建订单");
orderDao.create(order);
2,然后同时会调用库存模块storage,对相应手机的库存进行-1
log.info("--------》 订单微服务开始调用库存,做扣减---Count-");
storageService.decrease(order.getProductId(), order.getCount());
log.info("--------》 订单微服务开始调用库存,库存扣减完成!!");
3,对相应账户的余额进行扣减
log.info("--------》 订单微服务开始调用账户,账户扣减---money-");
accountService.decrease(order.getUserId(),order.getMoney());
log.info("--------》 订单微服务开始调用账户,账户扣减完成!!");
4,最后,修改订单状态,从0到1,表示此次校验成功
//修改订单状态,从0到1
log.info("--------》 订单微服务修改订单状态,start");
orderDao.update(order.getUserId(),1);
log.info("--------》 订单微服务修改订单状态,end");
log.info("--订单结束--");
然后同时调用我们用户模块,对该用户的余额进行扣除
例如:如果在account模块的service层对账户余额进行扣减的时候,我们Thread.sleep(20000)睡上20秒,这时候肯定会触发openfeign的超时机制,会导致扣款失败。
1,如果不加@GlobalTransactional全局事务注解,那么虽然扣款失败,但是订单仍然创建了,但是status属性并没有从0变为1.按说如果一个地方出错,订单就不应该被创建。
2,而在我们加上@GlobalTransactional全局事务注解后,就会发现,如果扣款失败,那么订单模块根本不会创建订单记录。
结论:分布式事务配置成功