seata1.0 achieve feign degradation and global exception handler when the transaction is rolled back

Source download

Here Insert Picture Description
You can direct micro-channel two-dimensional code scanning above my concern public number, which will then reply seata exception to the source code download address will also attach corresponding video tutorials, and regularly share relevant technical articles with you.

Foreword

In the process of our development, if an exception occurs, we will carry out normal try ... catch or configure global exception of fill be processed, or feign our points of service degradation, if this time we use distributed transactions seata then we will find that we the transaction will not be rolled back, do not use our distributed transactions we can not do exception handling yet? Obviously this is not possible, through the official program of the god to dynamically created by the AOP / close Seata distributed transaction we found a solution that works on our side of the article is based on Greenwich.SR2 seata1.0 and spring cloud-based version of realization of demo examples of distributed transactions the whole process of transformation on the basis of this article.

Configuring feign downgrade

Since we only need to verify that a downgrade to service, so this time we will directly test our order module account of service degradation, if service to feign degradation do not understand you can directly see this blog [spring cloud of Hoxton. achieve graceful degradation of the SR1 version feign], (https://linzhefeng23.blog.csdn.net/article/details/103710038) we create a impl package directly underneath feign order-server, as well as create a AccountApiImpl achieve AccountApi ,code show as below:

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


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

Then we modify our AccountApi added later our fallback modify the code as follows:

@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);
}

Finally, we modified our account-server module AccountServiceImpl decrease the method implemented directly thrown exception error simulation method calls back the process, subsequent code as follows:

@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("我出错了,会被回滚吗?");
    }
}

Finally, we need to modify the configuration file application.yml feign.hystrix.enabled properties of order-server is set to the value true, and finally start our seata-server, registry, account-server, order-server , storage-server, after starting out our direct access to the following address: http: // localhost: 8180 / order / create userId = 1 & productId = 1 & count = 10 & money = 100, this time we found our order-server trigger a downgrade transaction as follows? :
Here Insert Picture Description
then we found that we did not roll back the transaction, but executed properly, and that this is clearly not the result we want, how to do it this time, we directly refer to our great God to the official program dynamically created by AOP on / off Seata distributed transaction to solve the problem of our transactions are not rolled back.

Achieve feign demotion when the transaction is rolled back

Directly in our account-server, order-server, storage-server under the project to create a config directory, and then create our transaction processing section category [WorkAspect] code is as follows:

/**
 * @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();
        }
    }

}

At this time in the service of our account-server method of [simulated timeout exceptions, the global transaction rollback] this comment to let go, then direct access to: http: // localhost: 8180 / order / create userId = 1 & productId = 1 & count? = 10 & money = 100, and then we found our business to achieve a rollback, which achieve the effect we want.

After the realization of the global transaction rollback exception handling

We configured a [direct] GlobalExceptionsHandler in our account-server under the following code:

/**
 * @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 "全局异常错误给捕获了!";
        }
    }


}

This time we need to throw a run-in account-server service method when abnormal, then we direct access to: http: // localhost: 8180 / order / create userId = 1 & productId = 1 & count = 10 & money = 100, and then we find we? roll back the transaction realized, this achieves the effect we want.

Published 128 original articles · won praise 72 · Views 1.13 million +

Guess you like

Origin blog.csdn.net/linzhefeng89/article/details/103726590