文章目录
- 问题测试
- 1.bill模块抛异常,data模块正常
- 2.bill模块抛异常,data模块正常
- 解决方案
- 1.分布式事务
- 2.复制data的dao mapper到bill中
- 3.判断feign返回值,抛异常做回滚
最近在做一个财务系统,用到了两个模块bill账单模块和data数据模块,bill模块的数据是来自data模块的。
正确的流程是:bill模块更新数据状态,再用feign调用data模块更新数据状态
代码正常执行肯定是没问题的,但是测试的时候代码出问题了,抛出了异常,然后就发现一个模块数据更新到数据库了,另一个模块数据回滚了,造成了数据不一致问题。
修改了代码,进行测试
问题测试
1.bill模块抛异常,data模块正常
bill模块代码
@Transactional
public CommonResult verification(BillConfirmRequest request) {
//更新账单表
int update = billMapper.update(null, new LambdaUpdateWrapper<BillDO>()
.set(BillDO::getState, 9).eq(BillDO::getId, request.getId()));
//更新业务表
if (update > 0) {
//bill模块feign调用data模块
businessBillControllerFeign.verificationBillRpc(request);
throw new RuntimeException("testtttttttttttttt");
}
return CommonResult.error(VERIFICATION_FAIL);
}
data模块代码
@Transactional
public CommonResult verificationBillRpc(BillConfirmRequest request) {
//更新业务表
businessBillMapper.update(null,
new LambdaUpdateWrapper<BusinessBillDO>()
.set(BusinessBillDO::getState, 9)
.eq(BusinessBillDO::getBillId, request.getId()));
//其他业务处理
Collection<BusinessBillCommand> values = applicationContext
.getBeansOfType(BusinessBillCommand.class).values();
for (BusinessBillCommand serviceImpl : values) {
serviceImpl.verificationBill(request.getId(), request.getOwnerCode());
}
return CommonResult.ok("核销账单成功");
}
结果:bill数据回滚,data数据更新到数据库
2.bill模块抛异常,data模块正常
bill模块代码
@Transactional
public CommonResult verification(BillConfirmRequest request) {
//更新账单表
int update = billMapper.update(null, new LambdaUpdateWrapper<BillDO>()
.set(BillDO::getState, 9).eq(BillDO::getId, request.getId()));
//更新业务表
if (update > 0) {
//bill模块feign调用data模块
return businessBillControllerFeign.verificationBillRpc(request);
}
return CommonResult.error(VERIFICATION_FAIL);
}
data模块代码
@Transactional
public CommonResult verificationBillRpc(BillConfirmRequest request) {
//更新业务表
int update = businessBillMapper.update(null,
new LambdaUpdateWrapper<BusinessBillDO>()
.set(BusinessBillDO::getState, 9)
.eq(BusinessBillDO::getBillId, request.getId()));
if (update>0){
throw new RuntimeException("testtttttttttttttt");
}
//其他业务处理
Collection<BusinessBillCommand> values = applicationContext
.getBeansOfType(BusinessBillCommand.class).values();
for (BusinessBillCommand serviceImpl : values) {
serviceImpl.verificationBill(request.getId(), request.getOwnerCode());
}
return CommonResult.ok("核销账单成功");
}
结果:bill更新到数据库,data数据回滚
网上查资料得到原因:
微服务中,某事务内通过feign调用多个api,但Transcational注解的事务只对本服务的流程有效,feign调用的接口不会回滚。
解决方案
1.分布式事务
首先想到的就是分布式事务,但是这个系统不是很复杂,使用分布式事务大大增加了系统的复杂性,在这个系统不适用,大系统首选。
2.复制data的dao mapper到bill中
就是讲data模块的model dao mapper复制到bill模块中,这样bill也能直接操作这几张表了,就不用feign调用data再操作表了,但是model dao mapper各有10个多,要是都复制过去显得很冗余很丑陋了
3.判断feign返回值,抛异常做回滚
bill模块代码
@Transactional
public CommonResult verification(BillConfirmRequest request) {
//更新账单表
int update = billMapper.update(null, new LambdaUpdateWrapper<BillDO>()
.set(BillDO::getState, 9).eq(BillDO::getId, request.getId()));
//更新业务表
if (update > 0) {
CommonResult commonResult = businessBillControllerFeign.verificationBillRpc(request);
//这里判断feign的返回值,10001是全局异常的code
if(commonResult.getCode()==10001){
throw new RuntimeException("回滚");
}
}
return CommonResult.error(VERIFICATION_FAIL);
}
data模块代码
@Transactional
public CommonResult verificationBillRpc(BillConfirmRequest request) {
//更新业务表
int update = businessBillMapper.update(null, new LambdaUpdateWrapper<BusinessBillDO>()
.set(BusinessBillDO::getState, 9).eq(BusinessBillDO::getBillId, request.getId()));
if (update > 0) {
throw new RuntimeException("testtttttttttttttt");
}
//其他业务处理
Collection<BusinessBillCommand> values = applicationContext
.getBeansOfType(BusinessBillCommand.class).values();
for (BusinessBillCommand serviceImpl : values) {
serviceImpl.verificationBill(request.getId(), request.getOwnerCode());
}
return CommonResult.ok("核销账单成功");
}
bill会判断feign的返回值,如果data模块抛出异常,代码回滚,异常会被全局异常捕获,封装成code=10001的返回值,只需要判断如果code值为10001,抛出个异常就能回滚bill模块代码,这样也可以实现多模块feign回滚