事务
流程引擎以事务的方式执行流程。 BPMN 事务(transaction) 是一个逻辑工作单元,将一组活动组合在一起, 确保一组所有的活动都成功,或者如果任何活动失败, 则已完成的活动将通过补偿操作撤消。
事务是指当触发操作(例如启动流程、完成任务或发出执行信号)时, 引擎在流程中前进,直到在所有激活的执行路径上遇到等待状态。 更具体地说, 引擎在流程图中执行深度优先搜索, 并且在每个执行分支上都达到 等待状态 时返回。所有途径的活动都在一个事务中。 下图对此进行了说明:
我们可以看到,这是 BPMN 流程图的一部分,其中包含了一个用户任务、一个服务任务以及一个定时器任务。 用户任务和服务任务是同一工作单元的内容, 因此这两个任务应该以原子方式成功或失败。
即,如果服务任务抛出异常,我们必须回滚当前事务, 执行过程就会回溯到用户任务,并且用户任务仍然存在于数据库中。
在(1)中,应用程序或客户端线程完成任务。 在同一线程中, 流程引擎现在正在执行服务任务并前进,直到遇到等待状态, 在本例中,等待状态为定时器事件(2)。 然后它将控制权返回给调用者(3), 并提交事务。
等待状态
在 BPMN 中,一个 等待状态(waiting state) 指的是流程到达某个点时停止前进了, 需要等待一个事件或触发器才能继续。 这个状态可能由于各种原因发生,例如等待消息、定时器事件或用户输入。
流程中的等待状态包括:
-
用户任务
-
定时器事件
-
消息捕获事件
-
信号捕获事件
-
带外部 worker 的服务任务
-
基于事件的网关
异步延续
有时我们需要对流程中的事务边界进行自定义控制, 以便能够确定逻辑工作单元的范围。 这就是 异步延续(asynchronous continuations) 的作用。 在以下流程片段中:
在这个案例中,我们先完成用户任务,然后生成发票,最后将发票发送给客户。 这次,生成发票的任务与用户任务不在同一工作单元, 因此,如果生成发票失败,我们不希望回滚已经完成的用户任务。 我们希望流程引擎这么操作:完成用户任务(1), 提交事务并将控制权返回给调用应用程序。
然后,我们希望在后台异步线程中生成发票。 该后台线程是一个 作业执行器(实际上是一个线程池), 会定期轮询数据库查找需要处理的作业。 因此,实际当流程到达 “生成发票” 任务时,我们为流程引擎创建了一个需要生成作业的 “消息”, 告诉流程引擎稍后再继续流程并将作业写入数据库。 然后,该作业由作业执行器加载并执行。
更多详情,请参考 Flowable 的文档: 异步延续。
异步执行属性
流程引擎支持活动、网关、事件的下列属性。 在建模器中,可以在元素属性的 Asynchronous execution 部分设置:
-
Asynchronous (
flowable:async) — 流程引擎在元素执行之前提交当前事务。然后作业执行器执行该元素,并在一个新事务中继续推进流程。 -
Exclusive (
flowable:exclusive) — 为 Asynchronous 创建的作业是 排他性的:作业执行器不会将其与同一流程实例的其他排他性作业并发执行。默认选中。仅在选中 Asynchronous 时启用。 -
Asynchronous leave (
flowable:asyncLeave) — 该元素在当前事务中执行。流程引擎在离开该元素之前提交事务。然后作业执行器完成该元素,并在一个新事务中继续推进流程。 -
Asynchronous leave exclusive (
flowable:asyncLeaveExclusive) — 与 Exclusive 相同,适用于为 Asynchronous leave 创建的作业。默认选中。仅在选中 Asynchronous leave 时启用。
例如,以下服务任务同时具有 flowable:async 和 flowable:asyncLeave,因此将在其专属的独立事务中执行,与流程的前续和后续部分完全隔离开来:
<serviceTask id="generateInvoiceTask" name="Generate invoice"
flowable:async="true"
flowable:asyncLeave="true" />
排他作业
默认情况下,异步作业都是排他性的:作业执行器不会并发执行属于同一个流程实例的多个排他作业。 但不同流程实例的作业是可以并发执行的。
若要允许作业执行器并发执行属于同一流程实例的某个作业,请将 flowable:exclusive 或 flowable:asyncLeaveExclusive 设置为 false。
例如,以下服务任务位于 并行网关 之后的某条并行路径上:
<serviceTask id="reserveStockTask" name="Reserve stock"
flowable:async="true"
flowable:exclusive="false" />
当带有非排他作业的各条并行路径同时到达汇聚网关,其中某个事务可能会因为触发乐观锁异常而失败,随后作业执行器会重新尝试执行该失败的作业。 因此,请务必确保只有在元素中的工作允许被安全地重复执行时,才将作业设置为非排他性。 更多详情,请参考 Flowable 的文档: 排他作业。
失败重试
在 BPMN 中,失败重试(fail retry) 是指处理失败的服务任务的一种机制。 当任务由于发生错误而失败时, BPMN 引擎可以根据预定义的配置自动重试执行该任务。 配置有两个参数:
-
重试次数
-
重试间隔
如果任务返回 BPMN 错误,作业执行器将等待特定的时间,然后尝试再次执行该任务。 如果执行次数达到最大重试次数,但任务仍然失败,则会创建一个故障(incident), 可以进行手动干预或做进一步错误处理。
|
失败重试仅适用于异步任务。 |
|
对于同步服务任务,流程引擎不会处理重试。 当同步任务失败时,会抛出异常并停止流程实例。 |