事务

流程引擎以事务的方式执行流程。 BPMN 事务(transaction) 是一个逻辑工作单元,将一组活动组合在一起, 确保一组所有的活动都成功,或者如果任何活动失败, 则已完成的活动将通过补偿操作撤消。

事务是指当触发操作(例如启动流程、完成任务或发出执行信号)时, 引擎在流程中前进,直到在所有激活的执行路径上遇到等待状态。 更具体地说, 引擎在流程图中执行深度优先搜索, 并且在每个执行分支上都达到 等待状态 时返回。所有途径的活动都在一个事务中。 下图对此进行了说明:

transactions 1

我们可以看到,这是 BPMN 流程图的一部分,其中包含了一个用户任务、一个服务任务以及一个定时器任务。 用户任务和服务任务是同一工作单元的内容, 因此这两个任务应该以原子方式成功或失败。

即,如果服务任务抛出异常,我们必须回滚当前事务, 执行过程就会回溯到用户任务,并且用户任务仍然存在于数据库中。

在(1)中,应用程序或客户端线程完成任务。 在同一线程中, 流程引擎现在正在执行服务任务并前进,直到遇到等待状态, 在本例中,等待状态为定时器事件(2)。 然后它将控制权返回给调用者(3), 并提交事务。

等待状态

在 BPMN 中,一个 等待状态(waiting state) 指的是流程到达某个点时停止前进了, 需要等待一个事件或触发器才能继续。 这个状态可能由于各种原因发生,例如等待消息、定时器事件或用户输入。

流程中的等待状态包括:

  • 用户任务

  • 定时器事件

  • 消息捕获事件

  • 信号捕获事件

  • 带外部 worker 的服务任务

  • 基于事件的网关

异步延续

有时我们需要对流程中的事务边界进行自定义控制, 以便能够确定逻辑工作单元的范围。 这就是 异步延续(asynchronous continuations) 的作用。 在以下流程片段中:

transactions 2

在这个案例中,我们先完成用户任务,然后生成发票,最后将发票发送给客户。 这次,生成发票的任务与用户任务不在同一工作单元, 因此,如果生成发票失败,我们不希望回滚已经完成的用户任务。 我们希望流程引擎这么操作:完成用户任务(1), 提交事务并将控制权返回给调用应用程序。

然后,我们希望在后台异步线程中生成发票。 该后台线程是一个 作业执行器(实际上是一个线程池), 会定期轮询数据库查找需要处理的作业。 因此,实际当流程到达 “生成发票” 任务时,我们为流程引擎创建了一个需要生成作业的 “消息”, 告诉流程引擎稍后再继续流程并将作业写入数据库。 然后,该作业由作业执行器加载并执行。

要使用此功能,请设置该元素的 flowable:async 属性。 在建模器中,勾选元素属性中的 Asynchronous 复选框,例如,对于 服务任务。

更多详情,请参考 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), 可以进行手动干预或做进一步错误处理。

失败重试仅适用于异步任务。

对于同步服务任务,流程引擎不会处理重试。 当同步任务失败时,会抛出异常并停止流程实例。