1.问题描述
开启全局事务demo:
/**
- @Description: 同步数据
- @Param: [schemeSyncVo]
- @return: com.vulcan.base.domain.R<java.lang.Boolean>
- @Author: Lvlin.Lou
- @Date: 2029/9/16 10:59
*/
@GlobalTransactional
@Transactional
@Override
public R synchronousDataRelation(SchemeSyncVo schemeSyncVo) {
resourceService.synchronousDataRelation(schemeSyncVo);
changeDemandStatus(schemeSyncVo.getUserDemandId(),IntegerUtils.ONE,IntegerUtils.ONE);
return R.ok(Boolean.TRUE);
}
@GlobalTransactional在synchronousDataRelation方法上开启全局事务
resourceService.synchronousDataRelation(schemeSyncVo) 调用Feign接口,在资源微服务中添加数据同步关系(insert)
changeDemandStatus私有方法在项目微服务中修改需求状态(update)
无异常全局提交没有问题
changeDemandStatus 内有异常 全局回滚没有问题
resourceService.synchronousDataRelation(schemeSyncVo)内抛出异常,调用方changeDemandStatus提交了(没有回滚),被调用方resourceService.synchronousDataRelation(schemeSyncVo) 内部回滚了,造成数据业务状态不一致
2.问题原因
当被调用方内部抛出异常时,由于全局异常处理、feign接口降级导致调用方GlobalTransactional感知不到异常,故而不会触发调用方异常回滚,最终出现调用方提交而被调用方回滚的问题,导致数据不一致
3.解决方案
3.1 全局异常处理情况
解决思路:由于全局异常处理,导致被调用方在出现异常的情况下,返回的仍是框架统一的返回结果类,只是code不是200,所以在feign的decoder中加个返回结果状态判断逻辑,如果code不是200,就抛出一个自定义异常即可,其中返回结果状态判断逻辑可根据实际情况自己处理,比如判断msg的值是否为SUCCESS等等。
基础代码如下:
…安全处理
import feign.Response;
import feign.codec.Decoder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
/**
- @program: vulcan-project
- @description: ResultStatusDecoder
- @author: Lvlin.Lou
- @create: 2029-09-24 21:38
**/
@Slf4j
public class ResultStatusDecoder implements Decoder {
final Decoder delegate;
public ResultStatusDecoder(Decoder delegate) {
Objects.requireNonNull(delegate, "Decoder must not be null. ");
this.delegate = delegate;
}
@Override
public Object decode(Response response, Type type) throws IOException {
String resultStr = IOUtils.toString(response.body().asInputStream(), StandardCharsets.UTF_8);
log.info(“feign response interceptor ,result msg ->{}”,resultStr);
R result = GsonUtil.strToJavaBean(resultStr,R.class);
if (Constants.FAIL.equals(result.getCode())) {
throw new ApiException(result.getMsg());
}
// 回写body,因为response的流数据只能读一次,这里回写后重新生成response
return delegate.decode(response.toBuilder().body(resultStr, StandardCharsets.UTF_8).build(), type);
}
}
3.2 服务降级情况
解决思路:直接在降级方法中抛出自定义异常即可
基础代码如下:
/**
- @Description: 数据方案-新增修改
- @Param: [schemeSaveVO]
- @return: com.vulcan.base.domain.R<java.lang.String>
- @Author: Lvlin.Lou
- @Date: 2029/8/28 18:04
*/
@Override
public R saveScheme(SchemeSaveVO schemeSaveVO) {
throw new ApiException(FallbackUtil.buildApiExceptionMsg(throwable,Thread.currentThread().getStackTrace()[1].getClassName(),Thread.currentThread().getStackTrace()[1].getMethodName()));
}
FallbackUtil.buildApiExceptionMsg为自定义的构造异常信息方法
ApiException为自定义异常
两者都可以自己灵活处理