camunda 错误解决、补偿机制和分布式事务支持
前言
有段时间没写文章了,这段时间一直在看camunda服务编排的少量东西,之前没有接触过工作流引擎和服务编排相似的东西,花了一点时间来熟习和了解。因为我们需要运用camunda来做服务编排,因而对于一个编排,其中的工作单元是分布在多个微服务内部的业务调用,如何在服务编排中保持分布式事务的支持就成了我的关注点。camunda提供了补偿机制和对应的SAGA 模式来完成分布式事务的支持。本篇文章将对这一块进行形容和讨论。
错误解决机制
对于分布式事务来说,归根结底我们要考虑的就是,微服务调用出错的时候,我们的流程应该如何流转,并在这个流转过程中保证事务的一致性。因而我们第一个考虑的就是camunda本身的错误解决机制是什么。
事务回滚
第一种错误解决方式是事务回滚,要了解这个方式我们首先要看看camunda中事务是怎样组织的。在camunda中,流程的运行是被封装在一个个用户线程(client thread)中。可以这么了解,当一个启动或者者继续流程的请求到来之时,在我们的服务器的解决线程中(相似于tomcat中的http-thread-pool开头的那些线程),我们通过调用相似于下面的API
runtimeService.startProcessInstanceByKey(…)
来发起一个流程,这时候整个流程就完全运行在http-thread-pool线程中,这就称之为我们借用了一个用户线程,在这个线程中,camunda始终会开启一个数据库事务,并在这个事务下进行流程的运转。
在这个情况下,流程会一直运转到下一个等待状态(wait status 代表流程会在后面的某个时期继续),而后完成状态的持久化到数据库中,结束事务。在bpmn标准中,有下列几个等待状态:
- Tasks:
- 接受任务(receive task)
- 客户任务(user task)
- 外部服务任务(service task: external task)
- Events:
- 消息事件(message event)
- 定时事件(timer event)
- 信号事件(signal event)
- Gateway
- 基于时间的逻辑门(Event Based Gateway)
我们可以通过下图来了解这个过程:
事务模型
第一步,我们发起了完成task的请求,而后流程继续运行直到到达了定时等待事件,此时这个用户线程中的工作流运行已经结束,我们完成当前的事务并提交,而后返回控制给调用方,等待定时任务的触发。
这是正常情况,当我们出现异常的时候会怎样办呢?我们通过下图来进行了解:
异常事务回滚
当进行到发送任务(send invoice to customer)时触发了异常,这时候用户线程里的事务会完全得到回滚,而后把异常信息抛出给调用方,当下次该流程的请求进来的时候,会发现流程的状态点依然是最开始的那个客户任务(provide shipping address)。这个就相似于游戏中的保存点,玩过游戏的同学都知道,我们在这个保存点到下一个保存点游戏过程中,假如出现什么意外(游戏人物挂了,机器死机了,老妈回来了),我们下次进入游戏,还是会回到最近的一个保存成功的保存点,继续玩。
这个错误解决机制可以说是非常简洁和直观了,它异常解决完全交给了调用方,并保证了状态的回滚,但是并没有做出任何分布式事务一致性的支持。比如说假如一个流程 A -> B-> C,当C抛出异常的时候,我们并没有提供把A和B任务中的操作回滚或者者修正的能力,因而这个方案只是一个最初级的方案。
异常捕获和逻辑判断
我们知道我们自己diy的task,实际上就是一段段java代码,因而,我们可以在我们的代码中捕获异常,并且通过设置执行上下文中的参数,来标定我们的程序遭遇异常,一次来作为逻辑判断的依据,如下图所示:
异常捕获和逻辑判断
这种方式怎样说呢,是个方案,但是对于我们代码的耦合较高,而且对于我们流程来说会造成比较大的干扰,由于假如你要做细粒度,task级别的错误排查和修正,你就免不了在每个task后面都加上这么一层逻辑判断,会对编排流程本身的逻辑带来困扰,这样做出来的流程图也不够清晰。
BPMN 2.0 错误事件
针对上述方案的局限性,在BPMN 2.0规范中给出了错误事件来作为显式的解决错误的方案。以下图为例:
错误事件
一个典型的场景是假如我们要针对一个子流程中的错误来做出解决,一个比较简单的方案是在这个子流程上叠加一个错误边界事件(error boundary event),通过定义这样一个事件可以针对子流程中抛出的错误来进行额外的解决。需要注意的是,在这个图中,假如出发了错误边界事件 进入到错误解决流程,那本身的执行过程将会遭到中断(interrupting),因而错误事件也被称作是一种中断事件(interrupting event),关于这一块我在后续文章中会提及。
其实大家仔细看也会发现,错误事件,其实可以用前面的异常捕获和判断来等价完成,比方下图:错误解决
因而我们可以知道,错误事件其实可以看做是我们针对错误的一个简化的建模手段,并且从语义上能够给使用者一个清晰的感受,明晰了错误解决流程和正常业务解决流程的边界。
补偿机制
在理解了camunda的错误解决机制之后,我们就会想在错误解决机制之上完成task的修正补偿并最终支持分布式事务,因而BPMN规范提出了补偿事件(compensation event),通过对任务或者者子流程定义一个compensation事件 和它对应的解决单元来完成修正补偿。camunda官方也提供了一个demo,github地址在此 。我们可以来分析下:
补偿机制
在上述这个流程中,每个task都有一个补偿边界事件(compensation boundary event),按照补偿事件的定义,补偿事件只会在他标识的task成功执行完毕之后才有可能被触发(A compensation boundary event can only be triggered after the activity to which it is attached completes successfully.)。这个跟之前一般的边界事件是不一样的。而后还有一个需要注意的是,在上面的流程图中有一个虚线框框定的区域,这个区域在BPMN规范中被称为消息驱动的子流程(event-driven subprocess)。这个子流程的用处就是在整个流程中假如出现了某个消息,就会触发这个流程。
在上面这个流程里面,这个消息驱动的子流程的开始事件是一个错误事件(error start event),因而,一旦流程中出现了错误(错误类型需要在错误事件中指定),这个消息子流程将就会启动,而本身的执行过程就会被中断(大家还记得我们之前说错误事件是个中断性的事件)。在错误事件触发消息子流程之后,会抛出一个补偿事件,这个事件会触发每个task下定义补偿事件解决器,并最终结束这个流程。以Book flight任务出错为例,一旦出现了异常,消息子流程启动了补偿机制,这时候因为Book hotel和Reserve car已经执行完成,他们对应的补偿事件解决任务Cancel car和Cancel hotel会被执行(注意执行顺序会反过来)。这就是camunda所形容的对分布式事务一致性进行支持的SAGA 模式。
这个补偿机制提供了一个基本的分布式事务一致性的支持,但是会有个问题,就是通过消息子流程并不能完成对于流程的一个管控(也有可能是我还没学到位)。比如说我在一个任务A执行完成之后继续执行了一串操作而后再这段操作中假如出现了异常,我不仅希望想有补偿机制对这一段操作进行补偿,还要返回我最开始的任务A(相当于之前保存点机制),目前消息子流程就没法做了。因而,我们就需要借助一个更强的工具,叫事务性子流程(transaction subprocess)。
事务性子流程
按照BPMN规范的定义,事务性子流程用来定一个业务事务,当一个业务事务被认定为失败的时候,这个事务中的任务即可以撤销已经完成的操作(通过之前说的补偿机制)。为了表示一个业务事务失败,BPMN提供了一个新的事件:撤销事件(cancel event)。撤销事件有两个形态:
- 当它作为业务事务子流程的终态的时候,表明这个子流程是失败的,需要回滚,继而触发流程中各个任务所定义的补偿解决
当它作为业务事务子流程的边界事件的时候,标明这个子流程被回滚了,继而触发外部流程的回滚解决流程,和错误事件一样,撤销事件是中断的,即它会中断外部流程的正常执行路径
在上述概念引入之后,我们即可以做到一个分布式事务和流程的完整控制了,以下图为例:事务性子流程和撤销事件
在这个流程中,在启动流程之后我们会到达一个接收等待任务,作为我们流程的一个保存点,在接收到指定的消息触发流程继续执行进入到了一个事务性子流程中,在这个子流程中我们会经过两个任务,当第二个任务出错的时候,我们会到达子流程的撤销终态,这会触发我们第一个任务的补偿解决,最终保证我们子流程事务的一致性。在此之后,子流程结束并触发边界上的撤销事件,这时候外部的流程会进入我们的撤销解决逻辑,即进入到log cancel任务中,并最终回到了我们的接收消息任务,即回到了我们之前的保存点,完成了一个事务的撤销和逻辑的流转。
结语
本文对于camunda中的错误解决、补偿机制和分布式事务的支持以及具体的流程设计做了说明,有兴趣的同学可以自己下来实验一下。另外关于最后一个demo我已经把代码放到了这里,欢迎大家尝试和交流。
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » camunda 错误解决、补偿机制和分布式事务支持