框架源码研习:分布式事务源码心得

此分布式框架为TCC(try、confirm、cancel):是最终一致性事务解决方案,也就是所谓的柔性事务。

一:一切的开始始于 HmilyTransactionBootstrap

由于实现类 ApplicationContextAware 接口,因此在 context容器启动后,会立即执行其 setApplicationContext()方法,如下:

 * 实现ApplicationContextAware 接口,会在容器初始化的时候,带入 ApplicationContext 容器进来
 * 这里也就是在容器启动后直接进行初始化工作,因此此处是一切开始的地方
 * @param applicationContext
 * @throws BeansException
 */
@Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
    //将 applicationContext设置进 SpringBeanUtils中,通过此utils调用 ioc中的 bean
    SpringBeanUtils.getInstance().setCfgContext((ConfigurableApplicationContext) applicationContext);
    start(this);
}

SpringBeanUtils是一个把类设置进context容器,以及从容器中取值的一个工具类,具体见 Hmily

之后 start()中调用 HmilyInitService 类,如下:

/**
 *
 * @FunctionalInterface:java8函数式接口,用于函数式编程,只能有一个public方法(default除外)
 *
 * 使用例子:
 * public interface XttblogService {
 * void sayMessage(String message);
 * }
 *
 * 那么我们现在就可以使用 Lambda 表达式来表示该接口的一个实现(注:JAVA 8 之前一般是用匿名类实现的):
 *  XttblogService xttblogService = message -> System.out.println("Hello " + message);
 *
 */
@FunctionalInterface
public interface HmilyInitService {

调用其方法:

/**
 * hmily initialization.
 * @param hmilyConfig {@linkplain HmilyConfig}
 *
 * 解析:Runtime.getRuntime().addShutdownHook: 在JVM销毁前执行一个线程,如可以在此线程中优雅的关闭线程池
 *
 */
@Override
public void initialization(final HmilyConfig hmilyConfig) {
    Runtime.getRuntime().addShutdownHook(new Thread(() -> LOGGER.info("hmily shutdown now")));
    try {
        //通过spi自定义注解来获取具体要实现的子类
        loadSpiSupport(hmilyConfig);
        hmilyCoordinatorService.start(hmilyConfig);
    } catch (Exception ex) {
        LogUtil.error(LOGGER, " hmily init exception:{}", ex::getMessage);
        System.exit(1);
    }
    //自定义图标:其实就是定义图标然后打印如:log.info这种打印出来即可
    new HmilyLogo().logo();
}

SPI:非常有用的一种实例化子类的方法,类似于 策略模式:

/**
 * load spi.
 *
 * @param hmilyConfig {@linkplain HmilyConfig}
 */
private void loadSpiSupport(final HmilyConfig hmilyConfig) {
    //spi serialize
    final ObjectSerializer serializer = ExtensionLoader.getExtensionLoader(ObjectSerializer.class)
            .getActivateExtension(hmilyConfig.getSerializer());

    //spi repository  private String hmilyConfig.repositorySupport = "db"; 
    final HmilyCoordinatorRepository repository = ExtensionLoader.getExtensionLoader(HmilyCoordinatorRepository.class)
            .getActivateExtension(hmilyConfig.getRepositorySupport());

    repository.setSerializer(serializer);
    //把实例化的HmilyCoordinatorRepository实现类注入到context中
    SpringBeanUtils.getInstance().registerBean(HmilyCoordinatorRepository.class.getName(), repository);
}

此处 HmilyCoordinatorRepository 默认初始化策略为 "db",因此 会通过spi去加载对应的 db实现类,具体:

ExtensionLoader : 

/**
 * 例:A.isAnnotationPresent(B.class);
 *     B类型的注解是否在A类上。
 * @param type
 * @param <T>
 * @return
 */
private static <T> boolean withExtensionAnnotation(final Class<T> type) {
    //判断传入的类上是否含有 HmilySPI的注解
    return type.isAnnotationPresent(HmilySPI.class);
}
/**
 * Gets activate extension.
 *
 * @param value the value
 * @return the activate extension
 *
 * private static final String PREFIX = "META-INF/services/";
 * 注:ServiceLoader:如果某个接口含有一系列实现类,此时 loadAll(type) 里面放入此父类的class如:               HmilyCoordinatorRepository.class
 *     再在 META-INF下面建一个 services文件夹,里面放入所有实现类的名字如:
 *
 *     org.dromara.hmily.core.spi.repository.FileCoordinatorRepository
 *     org.dromara.hmily.core.spi.repository.JdbcCoordinatorRepository
 *     此时调用 loadAll()方法就会获取到所有的子类,由此来实现参数的动态配置,面向对象,前提是这些类已经定义好,并且是其子类。
 *
 * kryo
 */
public T getActivateExtension(final String value) {
    ServiceLoader<T> loader = ServiceBootstrap.loadAll(type);
    return StreamSupport.stream(loader.spliterator(), false)
            .filter(e -> Objects.equals(e.getClass()
                    .getAnnotation(HmilySPI.class).value(), value))
            .findFirst().orElseThrow(() -> new HmilyException("Please check your configuration"));
}

形式:

获取事务保存方式为:JdbcCoordinatorRepository ,然后进行数据库初始化建表等操作:

@Override
public void start(final HmilyConfig hmilyConfig) {
    final String repositorySuffix = buildRepositorySuffix(hmilyConfig.getRepositorySuffix());
    //获取实例化类型
    coordinatorRepository = SpringBeanUtils.getInstance().getBean(HmilyCoordinatorRepository.class);
    //获取对应的数据库类型然后建表
    coordinatorRepository.init(repositorySuffix, hmilyConfig);
}
/**
 * jdbc类型初始化,主要是为了根据传入的配置进行建表操作
 * @param modelName modelName
 * @param txConfig
 */
@Override
public void init(final String modelName, final HmilyConfig txConfig) {
    try {
        final HmilyDbConfig hmilyDbConfig = txConfig.getHmilyDbConfig();
        if (hmilyDbConfig.getDataSource() != null && StringUtils.isBlank(hmilyDbConfig.getUrl())) {
            dataSource = hmilyDbConfig.getDataSource();
        } else {
            HikariDataSource hikariDataSource = new HikariDataSource();
            hikariDataSource.setJdbcUrl(hmilyDbConfig.getUrl());
            hikariDataSource.setDriverClassName(hmilyDbConfig.getDriverClassName());
            hikariDataSource.setUsername(hmilyDbConfig.getUsername());
            hikariDataSource.setPassword(hmilyDbConfig.getPassword());
            hikariDataSource.setMaximumPoolSize(hmilyDbConfig.getMaxActive());
            hikariDataSource.setMinimumIdle(hmilyDbConfig.getMinIdle());
            hikariDataSource.setConnectionTimeout(hmilyDbConfig.getConnectionTimeout());
            hikariDataSource.setIdleTimeout(hmilyDbConfig.getIdleTimeout());
            hikariDataSource.setMaxLifetime(hmilyDbConfig.getMaxLifetime());
            hikariDataSource.setConnectionTestQuery(hmilyDbConfig.getConnectionTestQuery());
            if (hmilyDbConfig.getDataSourcePropertyMap() != null && !hmilyDbConfig.getDataSourcePropertyMap().isEmpty()) {
                hmilyDbConfig.getDataSourcePropertyMap().forEach(hikariDataSource::addDataSourceProperty);
            }
            dataSource = hikariDataSource;
        }
        this.tableName = RepositoryPathUtils.buildDbTableName(modelName);
        //save current database type
        this.currentDBType = DbTypeUtils.buildByDriverClassName(hmilyDbConfig.getDriverClassName());
        executeUpdate(SqlHelper.buildCreateTableSql(hmilyDbConfig.getDriverClassName(), tableName));
    } catch (Exception e) {
        LogUtil.error(LOGGER, "hmily jdbc log init exception please check config:{}", e::getMessage);
        throw new HmilyRuntimeException(e);
    }
}

注:如果要自己定义表,使用 executeUpdate()原生jdbc连接操作:

private int executeUpdate(final String sql, final Object... params) {
    Connection connection = null;
    PreparedStatement ps = null;
    try {
        connection = dataSource.getConnection();
        ps = connection.prepareStatement(sql);
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, convertDataTypeToDB(params[i]));
            }
        }
        return ps.executeUpdate();
    } catch (SQLException e) {
        LOGGER.error("executeUpdate-> " + e.getMessage());
        return FAIL_ROWS;
    } finally {
        close(connection, ps, null);
    }

}

底层使用的connection还是  connection = dataSource.getConnection(); 而springBoot默认dataSource是  hikariDataSource,dataSource = hikariDataSource;

二:启动 Disruptor

 *
 * 实现 ApplicationListener<ContextRefreshedEvent> 接口,可以在容器一启动的时候就调用
 *
 */
@Component
public class StarterHmilyTransactionHandler implements HmilyTransactionHandler, ApplicationListener<ContextRefreshedEvent> {

 
@Override
public void onApplicationEvent(final ContextRefreshedEvent event) {
    if (hmilyConfig.getStarted()) {
        disruptorProviderManage = new DisruptorProviderManage<>(new HmilyConsumerTransactionDataHandler(),
                hmilyConfig.getAsyncThreads(),
                DisruptorProviderManage.DEFAULT_SIZE);
        disruptorProviderManage.startup();
    }

}

事务核心:

@Override
public Object handler(final ProceedingJoinPoint point, final HmilyTransactionContext context)
        throws Throwable {
    Object returnValue;
    try {//此处preTry是try之前的操作,封装了当前transaction,保存的participate就只有当前的主方法的comfirm以及cancel方法
        HmilyTransaction hmilyTransaction = hmilyTransactionExecutor.preTry(point);
        try {
            //execute try
            returnValue = point.proceed();
            //try阶段完成
            hmilyTransaction.setStatus(HmilyActionEnum.TRYING.getCode());
            //把transaction发布为生产者
            hmilyTransactionExecutor.updateStatus(hmilyTransaction);
        } catch (Throwable throwable) {
            //此处代表整个事务失败后进行的取消
            //if exception ,execute cancel 并且从缓存中删除当前已经取消了的缓存,同时把消费者中的状态置为删除
            final HmilyTransaction currentTransaction = hmilyTransactionExecutor.getCurrentTransaction();
            disruptorProviderManage.getProvider().onData(() -> hmilyTransactionExecutor.cancel(currentTransaction));
            throw throwable;
        }
        //此处代表整个事务成功后的确认提交 execute confirm
        final HmilyTransaction currentTransaction = hmilyTransactionExecutor.getCurrentTransaction();
        disruptorProviderManage.getProvider().onData(() -> hmilyTransactionExecutor.confirm(currentTransaction));
    } finally {
        HmilyTransactionContextLocal.getInstance().remove();
        hmilyTransactionExecutor.remove();
    }
    return returnValue;
}

关键:获取一个方法的全部信息,然后通过反射调用方法,进行补偿:

/**
 * 通过反射获取的类、方法名、参数、参数类型来进行方法调用
 * @param hmilyInvocation
 * @return
 * @throws Exception
 */
@SuppressWarnings("unchecked")
private static Object execute(final HmilyInvocation hmilyInvocation) throws Exception {
    if (Objects.isNull(hmilyInvocation)) {
        return null;
    }
    final Class clazz = hmilyInvocation.getTargetClass();
    final String method = hmilyInvocation.getMethodName();
    final Object[] args = hmilyInvocation.getArgs();
    final Class[] parameterTypes = hmilyInvocation.getParameterTypes();
    final Object bean = SpringBeanUtils.getInstance().getBean(clazz);
    return MethodUtils.invokeMethod(bean, method, args, parameterTypes);
}

而这个方法的封装类:

/**
 * TccInvocation 封装方法调用点.
 * @author xiaoyu
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class HmilyInvocation implements Serializable {

    private static final long serialVersionUID = -5108578223428529356L;

    @Getter
    private Class targetClass;

    @Getter
    private String methodName;

    @Getter
    private Class[] parameterTypes;

    @Getter
    private Object[] args;

}

三:最核心的是 dubbo协议的调用:

/**
 * impl dubbo filter.
 *
 * 实现 dubbo的 filter接口,当进行 dubbo rpc调用时,会调用invoke方法,进行后续处理
 *
 * @author xiaoyu
 */
@Activate(group = {Constants.SERVER_KEY, Constants.CONSUMER})
@SuppressWarnings("all")
public class DubboHmilyTransactionFilter implements Filter {

 

@Override
public Result invoke(final Invoker<?> invoker, final Invocation invocation) throws RpcException {
    String methodName = invocation.getMethodName();
    Class clazz = invoker.getInterface();
    Class[] args = invocation.getParameterTypes();
    final Object[] arguments = invocation.getArguments();
    Method method = null;
    Hmily hmily = null;
    try {
        converterParamsClass(args, arguments);
        method = clazz.getMethod(methodName, args);
        hmily = method.getAnnotation(Hmily.class);
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (Exception ex) {
        ex.printStackTrace();
        LogUtil.error(LOGGER, "hmily find method error {} ", ex::getMessage);
    }
    if (Objects.nonNull(hmily)) {
        try {
            final HmilyTransactionContext hmilyTransactionContext = HmilyTransactionContextLocal.getInstance().get();
            if (Objects.nonNull(hmilyTransactionContext)) {
                RpcMediator.getInstance().transmit(RpcContext.getContext()::setAttachment, hmilyTransactionContext);
                final Result result = invoker.invoke(invocation);
                //if result has not exception
                if (!result.hasException()) {
                   //构建参与者信息 Participant
                    final HmilyParticipant hmilyParticipant = buildParticipant(hmilyTransactionContext, hmily, method, clazz, arguments, args);
                    if (hmilyTransactionContext.getRole() == HmilyRoleEnum.INLINE.getCode()) {
                        hmilyTransactionExecutor.registerByNested(hmilyTransactionContext.getTransId(),
                                hmilyParticipant);
                    } else {
                        hmilyTransactionExecutor.enlistParticipant(hmilyParticipant);
                    }
                } else {
                    throw new HmilyRuntimeException("rpc invoke exception{}", result.getException());
                }
                return result;
            }
            return invoker.invoke(invocation);
        } catch (RpcException e) {
            e.printStackTrace();
            throw e;
        }
    } else {
        return invoker.invoke(invocation);
    }
}

注:下面是如何获取反射需要的信息:

@Override
public Object handler(final ProceedingJoinPoint point, final HmilyTransactionContext context) throws Throwable {
    if (HmilyActionEnum.TRYING.getCode() == context.getAction()) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Class<?> clazz = point.getTarget().getClass();
        Object[] args = point.getArgs();
        final Hmily hmily = method.getAnnotation(Hmily.class);
        HmilyInvocation confirmInvocation = null;
        String confirmMethodName = hmily.confirmMethod();
        String cancelMethodName = hmily.cancelMethod();
        if (StringUtils.isNoneBlank(confirmMethodName)) {
            confirmInvocation = new HmilyInvocation(clazz, confirmMethodName, method.getParameterTypes(), args);
        }
        HmilyInvocation cancelInvocation = null;
        if (StringUtils.isNoneBlank(cancelMethodName)) {
            cancelInvocation = new HmilyInvocation(clazz, cancelMethodName, method.getParameterTypes(), args);
        }
        final HmilyParticipant hmilyParticipant = new HmilyParticipant(context.getTransId(),
                confirmInvocation, cancelInvocation);
        hmilyTransactionExecutor.registerByNested(context.getTransId(), hmilyParticipant);
    }
    return point.proceed();
}
 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值