《架构世界》2020微服务刊:微服务分布式事务实战
上QQ阅读APP看书,第一时间看更新

2.分布式事务解决方案:servicecomb-pack

补偿方式

在讲servicecomb-pack之前先了解两个概念: 不完美补偿(saga)和完美补偿(tcc)。

1saga:不完美补偿,一般在系统中我们会专门为业务逻辑对应写一个补偿逻辑,如果业务逻辑执行失败,就会去执行这个补偿逻辑,我们称这个补偿逻辑为反向操作,这个反向操作同样会留下操作痕迹,例如:在银行系统中,客户去ATM取钱,银行会先对用户账户进行扣款操作,如果本次取钱不成功,银行系统会发出一笔冲正操作,将之前扣除的款项打回用户账户,这个冲正操作在交易记录里面是开源查询到的。

2tcc:完美补偿,cancel阶段会彻底清楚之前的业务逻辑操作,用户是感知不到的。例如:在一个交易平台去发起交易,首先在try阶段不会直接去扣除账户余额,而且去检查用户的额度并刷新额度,然后在confirm阶段才去真正操作账户。如果出现异常,那么在cancel阶段就需要去执行业务逻辑来取消try阶段产生的后果,释放在try阶段被占用的额度。整个过程只有等confirm执行完毕,交易才算完成。

servicecomb-pack

servicecomb-pack出自于华为微服务框架servicecomb,是一个开源的分布式事务最终一致性解决方案,该项目已交由Apache软件基金会孵化,目前已经在apache毕业了。0.3.0版本之前叫servicecomb-saga,现版本已经改名为servicecomb-pack

servicecomb-pack架构主要包含两个组件:alphaOmega

lalphaalpha其实就是一个server端,需要用户自行编译运行,它的作用就是上述中的分布式事务协调器,主要作用是和Omega客户端进行通讯,接收omega发过来的事务事件,然后进行持久化存储事务以及修改协调子事务的状态,从而保证全局事务中的所有子事务状态都一致,即要么全执行完成,要么全执行失败。

lomegaOmega端其实可以看成是一个微服务中内嵌的agent,主要作用是监控本地子事务的执行情况并向alpha-server端发送子事务执行事件以及传递全局事务ID,并在异常情况下会根据alpha下发的操作事件进行相应的补偿操作。

从上图中我们大致可以了解整个servicecomb-pack是如何运转的,但是有一个疑问点,alpha-server端是怎么知道多个Omega发送过来的子事务是属于同一个全局事务的呢?其实在分布式事务开始点会生成一个全局事务ID,然后在调用子事务所处的服务时,会把这个全局事务ID传递给子事务,然后alpha端会会把这个全局事务IDOmega传递过来的子事务事件绑定并持久化到数据库中,这样就会形成一个完整的事务调用链,我们通过这个全局事务ID就可以完整的追踪到整个分布式事务的执行情况。

Omega会以切面编程的方式向应用程序注入相关的处理模块,帮助我们构建分布式事务调用的上下文。 Omega在事务处理初始阶段处理事务的相关准备的操作,在事务执行完毕做一些清理的操作,例如创建分布式事务起始事件,以及相关的子事件,根据事务的执行的成功或者失败生产相关的事务终止或者失败事件。这样带来的好处是用户的代码只需要添加几个annotation来描述分布式事务执行范围,以及与本地的事务处理恢复的相关函数信息,Omega就能通过切面注入的代码能够追踪与本地事务的执行情况。 Omega会将本地事务执行的情况以事件的方式通知给Alpha。 由于单个Omega不可能知晓一个分布式事务下其他参与服务的执行情况,这样就需要Alpha扮演一个十分重要的协调者的角色。Alpha将收集到的分布式事务事件信息整理汇总,通过分析这些事件之间的关系可以了解到分布式事务的执行情况,Alpha通过向Omega下发相关的执行指令由Omega执行相关提交或恢复操作,实现分布式事务的最终一致性。

在了解的Pack实现的部分细节之后,我们可以从下图进一步了解ServiceComb Pack架构下,AlphaOmega内部各模块之间的关系图。[i]

整个架构分为三个部分,一个是Alpha协调器,另外一个就是注入到微服务实例中的Omega,以及AlphaOmega之间的交互协议,目前ServiceComb Pack支持Saga以及TCC两种分布式事务协调协议实现。

Omega包含了与分析用户分布式事务逻辑相关的 事务注解模块Transaction Annotation) 以及 事务拦截器(Transaction Interceptor); 分布式事务执行相关的事务上下文Transaction Context),事务回调Transaction Callback) ,事务执行器Transaction Executor);以及负责与Alpha进行通讯的事务传输Transaction Transport)模块。

事务注解模块是分布式事务的用户界面,用户将这些标注添加到自己的业务代码之上用以描述与分布式事务相关的信息,这样Omega就可以按照分布式事务的协调要求进行相关的处理。如果大家扩展自己的分布式事务,也可以通过定义自己的事务标注来实现。

事务拦截器这个模块我们可以借助AOP手段,在用户标注的代码基础上添加相关的拦截代码,获取到与分布式事务以及本地事务执行相关的信息,并借助事务传输模块与Alpha进行通讯传递事件。

事务上下文Omega内部提供了一个传递事务调用信息的一个手段,借助前面提到的全局事务ID以及本地事务ID的对应关系,Alpha可以很容易检索到与一个分布式事务相关的所有本地事务事件信息。

事务执行器主要是为了处理事务调用超时设计的模块。由于AlphaOmega之间的连接有可能不可靠,Alpha端很难判断Omega本地事务执行超时是由AlphaOmega直接的网络引起的还是Omega自身调用的问题,因此设计了事务执行器来监控Omega的本地的执行情况,简化Omega的超时操作。目前Omega的缺省实现是直接调用事务方法,由Alpha的后台服务通过扫描事件表的方式来确定事务执行时间是否超时。

事务回调OmegaAlpha建立连接的时候就会向Alpha进行注册,当Alpha需要进行相关的协调操作的时候,会直接调用Omega注册的回调方法进行通信。由于微服务实例在云化场景启停会很频繁,我们不能假设Alpha一直能找到原有注册上的事务回调,因此我们建议微服务实例是无状态的,这样Alpha只需要根据服务名就能找到对应的Omega进行通信。

事务传输模块负责OmegaAlpha之间的通讯,在具体的实现过程中,Pack通过定义相关的Grpc描述接口文件定义了TCC以及Saga的事务交互方法,同时也定义了与交互相关的事件。[ii]