分布式事务(3)实现TCC的基础技术

概述

通过前面的文章介绍,我们已经知道了分布式事务的几种常见的解决方案,从我的从业经历来看,大部分的企业采用TCC或者基于消息队列中间件的最终一致性这两种解决方案。本专栏也将重点剖析这两种方案的具体实现,下面我们先来了解一下分布式环境下微服务实现分布式事务TCC方案的一些基础支持技术。
我们知道TCC是Try、Confirm和Cancel三个阶段的简称,通过下图我们可以看到TCC方案的具体结构图(以用户下单为例)。在这里插入图片描述
可见要想实现TCC分布式解决方案,最重要的一点就是能够在执行Try阶段之后能够根据执行结果回调Confirm方法或者是Cancel方法,并且Confirm和Cancel方法需要开发者自己去实现,而且需要保证幂等性(可接受重复调用结果一致)。
在我们java技术栈中,AOP技术是最容易实现TCC的这种方案回调机制的,当然要想实现TCC,或者说要想实现一个高效稳定可靠易扩展的TCC方案,还需要更多的技术。下面我将写一个demo看看AOP怎样可以实现TCC方案的回调机制,为后面的具体实现提供技术参考思路。

Demo

package com.luke.study.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Descrtption 定义一个TCC注解
 * 运用在Try阶段方法上
 * @Author luke
 * @Date 2019/9/6
 **/
@Target(value = ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface LukeTCC {

    /**confirm阶段方法名*/
    String confirmMethod();

    /**cancel阶段方法名*/
    String cancelMethod();

}

package com.luke.study.annotation;

import java.io.Serializable;

/**
 * @Descrtption PaymentReq
 * @Author luke
 * @Date 2019/9/6
 **/
public class PaymentReq implements Serializable {

    private Integer number;

    public Integer getNumber() {
        return number;
    }

    public void setNumber(Integer number) {
        this.number = number;
    }

    @Override
    public String toString() {
        return "PaymentReq{" +
                "number=" + number +
                '}';
    }

}

package com.luke.study.annotation;

import org.springframework.stereotype.Service;

/**
 * @Descrtption TestService
 * @Author luke
 * @Date 2019/9/6
 **/
@SuppressWarnings("all")
@Service
public class TestService {

    /**
     * TCC Try方法
     * @param req
     */
    @LukeTCC(confirmMethod = "confirmPayment",cancelMethod = "cancelPayment")
    public void payment(PaymentReq req){
        System.out.println("---service---payment-------"+req);
        if(req.getNumber()%2 == 1){
            //模拟抛异常
            int n = 1/0;
        }
    }

    /**
     * TCC confirm方法
     * @param req
     */
    public void confirmPayment(PaymentReq req){
        System.out.println("---service---confirmPayment-------"+req);
    }

    /**
     * TCC cancel方法
     * @param req
     */
    public void cancelPayment(PaymentReq req){
        System.out.println("---service---cancelPayment-------"+req);
    }

}
package com.luke.study.annotation;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * @Descrtption LukeTCCAspect
 * @Author luke
 * @Date 2019/9/6
 **/
@Component
@Aspect
public class LukeTCCAspect {

    @Pointcut("@annotation(com.luke.study.annotation.LukeTCC)")
    public void tccTry(){}

    /**
     * 成功调用完成Try方法
     * @param joinPoint
     */
    @AfterReturning("tccTry()")
    public void afterReturning(JoinPoint joinPoint){
        System.out.println("========Try成功调用confirm方法==========");
        //获取方法名
        String[] methodNames = getTCCConfirmMethoAndCancelMethod(joinPoint);
        String confirmMethodName = methodNames[0];
        //调用确认方法
        callMethod(joinPoint,confirmMethodName);
    }

    /**
     * 调用Try方法出现异常
     * @param joinPoint
     */
    @AfterThrowing("tccTry()")
    public void afterThrowing(JoinPoint joinPoint) {
        System.out.println("========Try失败调用cancel方法==========");
        //获取方法名
        String[] methodNames = getTCCConfirmMethoAndCancelMethod(joinPoint);
        String cancelMethodName = methodNames[1];
        //调用确认方法
        callMethod(joinPoint,cancelMethodName);
    }

    /**
     * 获取TCC确认方法名和取消方法名
     * @param joinPoint
     * @return
     */
    private String[] getTCCConfirmMethoAndCancelMethod(JoinPoint joinPoint){
        //获取调用的方法
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        //获取方法的注解内容
        LukeTCC lukeTCCAnno = method.getAnnotation(LukeTCC.class);
        String confirmMethodName = lukeTCCAnno.confirmMethod();
        String cancelMethodName = lukeTCCAnno.cancelMethod();

        //封装参数
        String[] result = new String[2];
        result[0] = confirmMethodName;
        result[1] = cancelMethodName;

        return result;
    }

    /**
     * 调用方法
     * @param joinPoint
     * @param methodName
     */
    private void callMethod(JoinPoint joinPoint,String methodName){
        //获取Try方法对象
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method tryMethod = methodSignature.getMethod();

        //请求参数
        Object[] args = joinPoint.getArgs();
        Parameter[] parameters = tryMethod.getParameters();
        Class[] parametersArray = new Class[parameters.length];
        for(int i = 0; i < parameters.length; i++){
            parametersArray[i] = parameters[i].getType();
        }

        try {
            //调用确认方法
            Object target = joinPoint.getTarget();
            Class<?> aClass = target.getClass();
            Method confirmOrCancelMethod = aClass.getMethod(methodName,parametersArray);
            confirmOrCancelMethod.invoke(target,args);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

@SpringBootApplication
public class App{

    public static void main( String[] args ) {
        ConfigurableApplicationContext context = SpringApplication.run(App.class, args);

        TestService testService = context.getBean(TestService.class);
        PaymentReq req = new PaymentReq();
        req.setNumber(88);
        testService.payment(req);

结果

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值