大家好,又见面了,我是你们的朋友全栈君。
- Hmily 源码解析(二)
前言
- 这一篇不想谈论Hmily源码的技术实现,而是想在过了一遍hmily的实现后把hmily的工作思路单独地整理出来再进行一次总结。看看能不能进一步有所得。
- 以hmily-demo-springcloud为例,它的实现思路如下。
Hmily事务工作流程
- 首先它是基于切面编程来实现分布式事务的操作,及通过日志记录TCC事务的信息以保证最终一致性。
- 前端发起的一个请求,第一次进入一个@Hmily注解的函数的时候,就进入Hmily的事务管理了。
- 这时会进入切面方法,生成一个日志实例(HmilyTransaction实例)。日志实例里面存储了所有后续需要操作的信息,比如流转状态,执行函数信息等等,并在后续的操作中会根据需要加入新的信息。
- 日志实例 有一个执行状态,一开始时是PRE_TRY(开始执行try),并会通过一个并发队列异步存储到数据库中。顺便说一句,每个微服务都有一个日志表,存储着对应的日志。
- 关于异步存储要多说一句,hmily在设计的时候把许多操作,尤其是对日志表的删改查操作都改用异步操作的方式,这也是hmily如此高效的一个原因,值得重点分析。
- 刚才说到日志实例被异步存储到数据库的日志表中了,而另一边就开始执行我们的业务函数。
- 如果函数内部再调用的函数仍有@Hmily注解,这时候切面里面不做其他任何操作。
- 如果调用的函数有@Hmily注解且是RPC函数,也就是调用其他微服务的代理接口,这时候会把事务id(transId 是事务唯一的,一个分布式事务id只有一个,且被用于日志主键),及当前的角色状态一起作为请求头的参数。
- 被调用的微服务接收到请求后,如果执行到带有@Hmily的函数,会根据传递过来的transId 的事务信息生成又一个事务日志信息,状态为PRE_TRY(开始执行try),并异步存储到数据库中对应该微服务的日志表中。
- 接着继续执行该微服务的主体方法
- 如果该微服务又调用了其它微服务,则同第7步到第12步。
- 如果执行成功,修改 事务日志的流转状态为TRYING(TRY阶段完成),如果失败了则删除日志抛出异常。
- 现在回到第一个微服务,如果调用成功,会把该rpc接口信息存储到日志信息里面。
- 如果还有调用其他微服务,则同第7步到13步。
- 如果所有的执行都没问题,这时候会把日志的状态改为TRYING(TRY阶段完成),然后发起异步任务执行confirm操作。
- confirm操作里会把状态改为CONFIRMING(“confirm阶段”),并异步存储到数据库中,然后通过反射存储在日志里面的confirmMethod方法,及调用rpc接口,将执行confirm的命令发送给对应的微服务。
- 其它微服务接收到confirm消息后,会根据该微服务的事务日志中存储的confirmMethod集合,一一执行,或再把该命令发送给被调用的下一层微服务,重复17步骤。
- 如果各个微服务在执行confirmMethod时,有失败的案例,会将失败的confirmMethod重新存储到对应的事务日志中,然后隔一定时间通过定时再次执行confirmMethod。直到一一成功或超过重试次数发出信息给维护人员处理。confirmMethod失败后的定时执行的这一步各个微服务已经是各自为政了,不用再自上而下的从第一个微服务发起任务。
- cancel方法同16步到18步。它的触发条件是,只要在try阶段里有哪个try出问题了,异常会层层抛出到最上层,后面的try都不执行。而前面执行过的try信息或调用过的rpc接口信息都会存储在事务日志中间。后面只要同confirm阶段一样,根据这些信息执行cancelMethod方法或对RPC接口发起cancel请求。
补充说明
- RPC上面的Hmily注解的作用只是用于连接前后两个微服务的,使它们处于同一个分布式事务之下。
- 各个微服务内部的@Hmily才是真正用于处理分布式事务的(执行try,confirm,canel,维护事务日志等)。
- 我觉得第12步有些问题。如果之前又调用了其它微服务,且也有事务,而且有事务是成功的,那在这边因为自己执行失败了一刀切的把日志信息删掉了。而被调用的微服务的事务再执行TRY后就无法再被执行了,因为中间断代了,无法接收到第一个发起者发下来的confirm命令或cancel命令,从而不知道该执行什么,最终导致数据不一致。
- 我觉得在设计上,hmily要求分布式事务相关的业务代码要非常纯粹,如果中间有什么伴随着时间会有不同结果的操作(比如查询),可能就会由于查询结果不的不同导致破坏了最终一致性。
- 之前有考虑过,同一个微服务内,会不会有两个@Hmily嵌套的情况。感觉有点傻,同一个微服务内部用hibernate事务管理就好了,不应该写成@Hmily嵌套,而且当前版本的@Hmily似乎也不支持这种做法,目前无法实现这种需求。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/143136.html原文链接:https://javaforall.cn