想象一个生活中的场景:在线预订一次旅行。
这个流程包含三个步骤:
预订机票(成功)
预订酒店(成功)
预订租车(失败,比如信用卡额度不足)
现在,租车失败了,但机票和酒店已经订好了。整个旅行计划泡汤了,你不能只放着订好的机票和酒店不管。你必须执行“反向操作”来撤销之前的成功步骤:
取消机票预订
取消酒店预订
在 Flowable (以及 BPMN 2.0 规范) 中,这种为已经成功完成的活动执行“反向操作”的机制,就叫做补偿(Compensation)。补偿事件就是用来触发和处理这些补偿行为的。
补偿的核心目的不是处理当前活动的失败,而是为了撤销一个或多个过去已经成功完成的活动。
这和“错误事件(Error Event)”有本质区别:
错误事件:处理的是当前活动执行时发生的错误。比如“预订租车”这个任务本身出错了。
补偿事件:处理的是因为后续流程出现了问题,需要去撤销之前已经成功的活动。比如“预订租车”失败后,去触发“取消机票”和“取消酒店”这两个反向操作。
一个完整的补偿机制通常由以下三部分组成:
- 需要被补偿的活动 (Activity to be Compensated)
这是流程中一个正常的、已经成功完成的活动。例如,“预订机票”这个用户任务或服务任务。要使其能够被补偿,你必须为它定义一个补偿处理器。 - 补偿处理器 (Compensation Handler)
这是真正的“反向操作”逻辑。它定义了如何撤销原始活动。
在BPMN图中,它通常是一个附加在原始活动边界上的补偿边界事件(Compensation Boundary Event),这个事件图标是一个向左的回退箭头符号。
从这个边界事件引出的流程就是补偿逻辑,例如连接到一个叫“取消机票预订”的服务任务。
重要:补偿处理器在原始活动成功完成时被“注册”或“关联”上,但并不执行。它只是静静地等待被将来的补偿事件触发。
(上图中,“Book Hotel”活动有一个补偿边界事件,它连接到“Cancel Hotel”这个补偿处理器。) - 触发补偿的事件 (Compensation Trigger)
这是启动补偿流程的“扳机”。主要有两种方式:
a. 补偿中间抛出事件 (Intermediate Throwing Compensation Event)
这是一个明确的指令,告诉流程引擎:“开始执行补偿!”。
你可以配置它来补偿所有在当前作用域(比如主流程或子流程)内已完成且定义了补偿处理器的活动。
也可以指定只补偿某一个特定的活动。
在我们的旅行预订例子中,当“预订租车”失败后,流程会走向一个“补偿中间抛出事件”,由它来触发“取消机票”和“取消酒店”这两个补偿处理器。
b. 补偿结束事件 (Compensation End Event)
如果一个子流程以“补偿结束事件”结尾,它会自动触发该子流程范围内所有已完成活动的补偿。
c. (隐式触发) 事务被取消
如果一个活动包含在一个**事务子流程(Transaction Sub-Process)**中,当这个事务因为错误或取消事件而被回滚时,Flowable会自动对该事务内所有已成功完成的活动执行补偿。这是补偿机制最强大的应用之一,常用于实现分布式事务中的SAGA模式。
业务场景描述
一个客户在线预订一个“机票 + 酒店”的旅游套餐。
系统首先尝试预订机票。
如果机票预订成功,系统接着尝试预订酒店。
如果酒店也预订成功,系统最后进行信用卡扣款。
核心问题:如果机票和酒店都预订成功了,但最后的信用卡扣款失败了(例如,额度不足、卡号无效等),那么整个订单是失败的。此时,必须自动取消之前已经成功预订的机票和酒店,以避免为客户和公司造成损失。
这个“取消”操作,就是补偿机制的完美应用场景。
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
xmlns:flowable="http://flowable.org/bpmn"
targetNamespace="http://flowable.org/bpmn/examples">
<process id="tripBookingCompensationProcess" name="旅游预订补偿流程" isExecutable="true">
<!-- 1. 流程开始 -->
<startEvent id="startEvent" name="开始预订"/>
<sequenceFlow id="flow1" sourceRef="startEvent" targetRef="bookFlightTask"/>
<!-- 2. 预订机票 (可被补偿的活动) -->
<serviceTask id="bookFlightTask" name="预订机票" flowable:class="org.flowable.example.BookFlightDelegate"/>
<sequenceFlow id="flow2" sourceRef="bookFlightTask" targetRef="bookHotelTask"/>
<!-- 3. 预订酒店 (可被补偿的活动) -->
<serviceTask id="bookHotelTask" name="预订酒店" flowable:class="org.flowable.example.BookHotelDelegate"/>
<sequenceFlow id="flow3" sourceRef="bookHotelTask" targetRef="chargeCreditCardTask"/>
<!-- 4. 信用卡扣款 (关键决策点) -->
<serviceTask id="chargeCreditCardTask" name="信用卡扣款" flowable:class="org.flowable.example.ChargeCardDelegate"/>
<sequenceFlow id="flow4" sourceRef="chargeCreditCardTask" targetRef="paymentGateway"/>
<!-- 5. 决策网关:判断扣款是否成功 -->
<exclusiveGateway id="paymentGateway" name="支付成功?"/>
<sequenceFlow id="flowSuccess" sourceRef="paymentGateway" targetRef="sendConfirmationTask">
<conditionExpression xsi:type="tFormalExpression" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><![CDATA[${paymentSuccess == true}]]></conditionExpression>
</sequenceFlow&g

最低0.47元/天 解锁文章
1418

被折叠的 条评论
为什么被折叠?



