seata1.0实现feign降级和全局异常处理的时候事务的回滚

源码下载

在这里插入图片描述
大家可以直接微信扫描上面的二维码关注我的公众号,然后回复seata exception 里面就会给到源代码的下载地址同时会附上相应的视频教程,并定期的与大家分享相关的技术文章。

前言

在我们开发的过程中,如果出现异常,正常我们都会进行try…catch或者配置全局异常的补获处理,或者我们的分feign的服务降级,如果这时候我们使用分布式事务seata那么我们会发现我们的事务不会回滚了,难道使用我们的分布式事务我们就不能做异常的处理了吗?很明显这是不可能的,通过官方大神给的方案通过AOP动态创建/关闭Seata分布式事务我们找到了解决方案,我们这边文章的工程是基于基于seata1.0和spring cloud的Greenwich.SR2版本的分布式事务demo例子的实现全过程这篇文章的基础上进行改造的。

配置feign的降级

由于我们只需要验证一个服务降级即可,那么我们这次就直接验证我们的订单模块的account的服务降级,若是对feign的服务降级有不懂的可以直接看这篇博客[spring cloud的Hoxton.SR1版本的feign的优雅降级的实现],(https://linzhefeng23.blog.csdn.net/article/details/103710038)我们直接在order-server的feign底下创建一个impl包,同时创建一个AccountApiImpl实现AccountApi,代码如下:

/**
 * @author linzf
 * @since 2019/12/27
 * 类描述:
 */
@Component
public class AccountApiImpl implements AccountApi {


    @Override
    public String decrease(Long userId, BigDecimal money) {
        System.out.println("我被服务降级了,回滚了吗?");
        return "我被服务降级了!";
    }
}

接着我们修改我们的AccountApi添加我们的fallback 修改以后代码如下:

@FeignClient(value = "account-server",fallback = AccountApiImpl.class)
public interface AccountApi {

    /**
     * 扣减账户余额
     * @param userId 用户id
     * @param money 金额
     * @return
     */
    @RequestMapping("/account/decrease")
    String decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);
}

最后我们修改我们的account-server模块的AccountServiceImpl的decrease方法直接在该方法的后面抛出异常,模拟方法调用出错的实现,修改以后代码如下:

@Service("accountServiceImpl")
public class AccountServiceImpl implements AccountService{

    private static final Logger LOGGER = LoggerFactory.getLogger(AccountServiceImpl.class);
    @Autowired
    private AccountDao accountDao;
    @Autowired
    private OrderApi orderApi;

    /**
     * 扣减账户余额
     * @param userId 用户id
     * @param money 金额
     */
    @Override
    public void decrease(Long userId, BigDecimal money) {
        LOGGER.info("------->扣减账户开始account中");
        //模拟超时异常,全局事务回滚
//        try {
//            Thread.sleep(30*1000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        accountDao.decrease(userId,money);
        LOGGER.info("------->扣减账户结束account中");

        //修改订单状态,此调用会导致调用成环
        LOGGER.info("修改订单状态开始");
        String mes = orderApi.update(userId, money.multiply(new BigDecimal("0.09")),0);
        LOGGER.info("修改订单状态结束:{}",mes);
        throw new RuntimeException("我出错了,会被回滚吗?");
    }
}

最后还需要修改order-server的application.yml的配置文件的feign.hystrix.enabled属性的值设置为true,最后启动我们的seata-server、注册中心、account-server、order-server、storage-server,启动完成以后我们直接访问以下的地址:http://localhost:8180/order/create?userId=1&productId=1&count=10&money=100,这时候我们发现我们的order-server触发了事务的降级处理如下所示:
在这里插入图片描述
然后我们发现我们的事务并没有回滚,而是正常执行了,那这很明显不是我们想要的结果,那这时候怎么办呢,我们直接参考我们的官方大神给的方案通过AOP动态创建/关闭Seata分布式事务来解决我们的事务不回滚的问题。

实现feign降级的时候事务的回滚

直接在我们的account-server、order-server、storage-server工程底下创建一个config目录,然后创建我们的事务处理切面类【WorkAspect】代码如下:

/**
 * @author linzf
 * @since 2019/12/27
 * 类描述: 用于处理程序调用发生异常的时候由于异常被处理以后无法触发事务,而进行的处理,使之可以正常的触发事务。
 */
@Aspect
@Component
public class WorkAspect {

    private final static Logger logger = LoggerFactory.getLogger(WorkAspect.class);

    @Before("execution(* io.seata.sample.service.*.*(..))")
    public void before(JoinPoint joinPoint) throws TransactionException {
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();
        logger.info("拦截到需要分布式事务的方法," + method.getName());
        // 此处可用redis或者定时任务来获取一个key判断是否需要关闭分布式事务
        GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
        tx.begin(300000, "test-client");
        logger.info("创建分布式事务完毕" + tx.getXid());
    }

    @AfterThrowing(throwing = "e", pointcut = "execution(* io.seata.sample.service.*.*(..))")
    public void doRecoveryActions(Throwable e) throws TransactionException {
        logger.info("方法执行异常:{}", e.getMessage());
        if (!StringUtils.isBlank(RootContext.getXID())) {
            GlobalTransactionContext.reload(RootContext.getXID()).rollback();
        }
    }

}

这时候我们account-server的service方法中将【模拟超时异常,全局事务回滚】这段的注释给放开,然后直接访问:http://localhost:8180/order/create?userId=1&productId=1&count=10&money=100,然后我们发现我们的事务实现了回滚了,这就达到了我们想要的效果。

实现全局异常处理以后事务的回滚

我们直接在我们的account-server底下配置一个【GlobalExceptionsHandler】,代码如下:

/**
 * @author linzf
 * @since 2019/5/29
 * 类描述:全局异常捕获处理
 */
@ControllerAdvice
public class GlobalExceptionsHandler {

    private Logger log = LoggerFactory.getLogger(GlobalExceptionsHandler.class);

    /**
     * 功能描述:全局异常处理
     *
     * @param e
     * @return 返回处理结果
     * @throws Exception
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Object errorHandler(Exception e) throws Exception {
        // 此处为属性级的错误日志的处理
        if (e instanceof ConstraintViolationException) {
            log.info("绑定错误日志为:{}", e.getMessage());
            return "请求数据格式错误";
            // 此处为方法级别的错误日志处理
        } else if (e instanceof MethodArgumentNotValidException) {
            log.info("方法级的绑定错误日志为:{}", e.getMessage());
            return "请求数据格式错误";
            // 此处为全局错误日志的处理
        } else {
            log.info("错误日志为:{}", e.getMessage());
            return "全局异常错误给捕获了!";
        }
    }


}

这时候我们需要在account-server的service方法中抛出运行时异常,然后我们直接访问:http://localhost:8180/order/create?userId=1&productId=1&count=10&money=100,然后我们发现我们的事务实现了回滚了,这就达到了我们想要的效果。

  • 7
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
MQ分布式事务feignseata实现分布式事务有一些区别。 首先,MQ分布式事务是通过消息队列实现的。它的作用是解耦、异步、削峰,实现分布式事务的最终一致性。MQ分布式事务是一种柔性事务的解决方案,适用于高并发场景。在MQ分布式事务中,事务参与者将事务消息发送到消息队列,消息队列再将消息异步分发给事务的其他参与者,各个参与者根据消息处理结果来决定是否提交或回滚事务。 而feignseata是另一种实现分布式事务的方式。Feign是一种轻量级的、声明式的HTTP客户端,可以方便地实现服务之间的远程调用。而seata是一个开源的分布式事务解决方案,它提供了一套完整的分布式事务管理功能。在使用feignseata实现分布式事务时,可以使用seata提供的分布式事务管理器来保证各个服务之间的事务一致性。 总的来说,MQ分布式事务feignseata实现分布式事务都可以实现分布式事务的一致性,但是它们的实现方式和适用场景有所不同。MQ分布式事务适用于高并发场景,而feignseata适用于服务之间的远程调用。具体使用哪种方式取决于实际的业务需求和场景。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [seata与MQ用分布式事务区别](https://blog.csdn.net/qq_39761320/article/details/109730112)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [分布式事务解决方案及Seata 1.6.1案例](https://blog.csdn.net/qq_42665745/article/details/130805466)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笨_鸟_不_会_飞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值