Hmily 源码解析(二)


TCC流程

  • 这篇我们按照try成功,confirm成功的流程来一步步看一下hmily究竟是如何实现这些功能的。
  • 首先我们要启动一下这四个微服务。

在这里插入图片描述

  • 接着我们访问hmily-demo-springcloud-order下的orderPay接口,count为1,amount为1

在这里插入图片描述

  • 随着程序的调用最终会调用到该方法makePayment,这个方法里面就是我们的分布式事务逻辑。我们可以看到一共有三个方法有@Hmily事务注解,一个就是makePayment方法本身,还有就是inventoryClient.decrease与accountClient.payment两个fegin接口方法。接下来我们就来看一下@Hmily注解是如何使这个分布事务保持最终一致的。
    在这里插入图片描述
  • 单独整理了一下Hmily事务的工作流程(不涉及技术分析),希望看完这个阅读如下内容时思路会更加清晰。

makePayment TRY流程

  • Hmily是通过切面来实现TCC的,那么我们先来看一下@Hmily切面逻辑
  • AbstractHmilyTransactionAspect @Hmily切面类
    • Hmily切面采用的是Around标签,说明在执行主代码前后Hmily框架都要做一些工作以保证事务
    • 而且AbstractHmilyTransactionAspect 只是抽象类,我们来看一下在springcloud下的实现类做了什么

在这里插入图片描述* SpringCloudHmilyTransactionAspect中做了两件事,定义切面的执行顺序为最优先执行,其次定义了hmily切面的实际拦截器类SpringCloudHmilyTransactionInterceptor,我们接着继续该类的interceptor方法,看看himly的分布式事务的实现

在这里插入图片描述

  • interceptor方法,这是一个非常核心的hmily事务方法了,后面的很多流程我们都要反复的讲到该方法,该方法负责定义当前事务的阶段与参与的角色类型
    • 我们先看第一句,类HmilyTransactionContext非常重要,它里面保存着三个字段,当前事务的id,当前执行的阶段(action),当前的角色(role)。由这两个字段(action,role)的组合hmily将执行对应的事务逻辑。另外HmilyTransactionContext是同线程绑定的,阶段与角色只在该线程内有效,当新建一个线程时,又会需要的HmilyTransactionContext实例,顺便先说一下try、confirm和cancel三个阶段是分别在三个线程上实现的。
    • 我们继续看HmilyTransactionContextLocal存储着当前线程的HmilyTransactionContext实例,很明显一开始的时候HmilyTransactionContextLocal中是没有当前线程的HmilyTransactionContext实例,所以hmilyTransactionContext变量为null,接着就到else部分
    • else部分里面的逻辑是从RequestAttributes(也就是请求参数)中获取HmilyTransactionContext实例,但是从前端传过来的参数只有count和amount,所以这里也将返回null,显然这段代码并不是为现在的我们而准备的,后面有用到再细说。
    • 所以invoke方法里的第一个参数目前仍是为null

在这里插入图片描述

  • 我们继续深入invoke方法,这也是一个很重要的方法,在这个方法中将根据不同的角色进行业务分流操作。也就是根据不同的角色执行不同的程序代码。
    • 我们看一下factoryOf方法内部,目前HmilyTransactionContext为null所以返回了StarterHmilyTransactionHandler.class实例,由名字我们可以看出这是一个开始事务的处理类。到这里我们就应该大概理解作者的思路了,为什么之前HmilyTransactionContext为什么一直是为null的,就是为了流转到StarterHmilyTransactionHandler.class实例。也只有刚开始一个事务的时候HmilyTransactionContext才会为null,也才会流转到StarterHmilyTransactionHandler.class实例,在这个实例中将帮我们创建一个HmilyTransactionContext实例。
    • 接着我们继续看SpringBeanUtils通过StarterHmilyTransactionHandler.class实行了父接口,并执行StarterHmilyTransactionHandler.handler方法。

在这里插入图片描述在这里插入图片描述

  • 前面这两小节是切面的主入口,控制不同业务逻辑的分流,后面会经常提到。下面将介绍四个分流中的第一个分流StarterHmilyTransactionHandler.handler,它的主要目的是用于发起一个分布式事务
  • 如下图StarterHmilyTransactionHandler.handler方法被我分解成了六个模块,分别是创建分布式事务,执行try业务主体,更新日志为try结束阶段,try失败发起异步cancel任务,try成功发起异步confirm任务,删除绑定线程的分布式事务信息。下面我将一个个介绍这六个模块

在这里插入图片描述

创建分布式事务日志
  • HmilyTransactionExecutor 是分布式事务的管理类,hmily的分布式事务是基于日志实现的,每一个原子事务(分布式事务的最小组成单位)就对应着一条事务日志,hmily通过对事务日志的维护来保证分布式事务的最终一致性,所以也可以说HmilyTransactionExecutor 是管理事务日志的类。

  • 我们接着看preTry内究竟做了什么
    在这里插入图片描述

  • 首先创建了一个hmilyTransaction实例, hmilyTransaction实例就是hmily分布式事务的日志对象,该类是最重要的一个类,里面维护事务操作所需要的信息,如角色、状态,重试次数等,以及try,confirm、cancel方法的信息封装为HmilyParticipant实例存储起来,以方便后面通过反射调用对应的方法。这些我们暂时都没用到,先按下不表。我们现在要关心的几个是,状态为pre_try(开始执行try),角色为START(发起者),传入的transId是为null,new HmilyTransaction()里会生成一个uuid作为当前事务的transId。但是还没有保存到数据库中,说明有了trainsId但是这个实例还没真正创建,不过有了trainsId这个分布式事务从这一刻起就有了一个唯一的标识。
    在这里插入图片描述

  • 第二步将hmilyTransaction实例存储到CURRENT中同当前线程绑定了,

  • 第三步使用hmilyTransactionEventPublisher类将hmilyTransaction


发布到日志异步操作任务上进行保存操作,该异步操作会将hmilyTransaction保存到数据库中(当然如果我们可以选择采用文件存储,或zookeeper等其他方式存储日志),记得上一章中我们在tcc库下简历的三张日志表么,hmilyTransaction实例就会被存储在对应的表中。hmilyTransactionEventPublisher里面究竟是如何实现异步保存日志的,后面我们会再起一章单独讲解。这边我们假定都没问题,而transId将在异步保存后生成到数据库中

  • 最后一步,根据hmilyTransaction创建HmilyTransactionContext实例,并且存储到HmilyTransactionContextLocal中,同当前线程绑定,transId目前已经有值了

  • 到这里我们可以简单的总结一下,之前的总总我们做了什么:生成trainsId,异步保存 hmilyTransaction实例(事务日志)到数据库中,生成HmilyTransactionContext实例保存到线程中

执行主体业务方法了
try成功执行
  • 根据前面执行完主体方法的说明,如果在执行主体方法,调用其他微服务的过程中没有任何异常那么执行结束后将会执行confirm方法阶段。也就是如下的语句,这里也用到了一个异步执行队列,它的作用就是异步执行如下语句

hmilyTransactionExecutor.confirm(currentTransaction)

在这里插入图片描述

  • 有意思的是这个方法我们在调用微服务的时候已经分析过了,它会遍历hmilyParticipants集合,执行hmilyParticipant实例里面存储的CONFIRMING方法,如果失败了会再把失败的集合定时重试。
  • 我们现在只需要确定一件东西就好了,hmilyParticipants里面有几项?
    1. 一个是本切面的创建hmilyTransaction实例时默认把当前的函数生成hmilyParticipant实例,执行的时候也就是调用如下黄框内所指代的函数
      在这里插入图片描述 2. 还有两个在 执行主体方法 时有说明,通过HmilyFeignBeanPostProcessor配置,在调用微服务时结束后,会生成对应的hmilyParticipant实例存储到hmilyParticipants集合中去。我们看到同上一个hmilyParticipant实例有些不同是下面的@Hmily注解里面没有描述confirmMethod和cancelMethd,这意味着执行这两个hmilyParticipant实例的confirmMethod方法时,还是同try时一样调用如下的rpc,但是它们的参数略有不同,就是执行状态变成了HmilyActionEnum.CONFIRMING(confirm阶段),根据 执行主体方法 里的说明,在那边的微服务就会根据这个状态调用那个微服务中的hmilyParticipants集合里所有实例的confirmMethod方法。
      在这里插入图片描述在这里插入图片描述
try失败执行cancel方法回滚
  • 与执行confirm流程一致,不再复述了。
删除绑定线程的事务信息
  • 由于线程是通过线程池分配出来使用的,我们的任务结束了但是线程并不一定会被销毁而是返还给线程池。如果不删除绑定线程的事务实例,则这些信息就会被下一个申请到该线程的任务所见。
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值