camunda如何实现流程重启和流程激活
我们在使用工作流的时候,经常遇到“流程激活”或者“流程重启”的需求,即流程实例已经结束,由于某些特殊业务需求,仍然需要重新激活重启该流程实例。Camunda流程平台提供了这样的机制和接口,可以实现流程重启的需求,流程实例终止后,其历史数据仍然存在,并且可以访问以恢复流程实例,前提是历史级别设置为FULL。本文以开源流程引擎Camunda7.19.0版本介绍流程重启的API接口,流程重启的应用场景可能是:
- 还原被错误取消的流程实例的最后状态
- 在错误决策导致终止后重新启动流程实例
- 特殊业务需要需要重启启动流程
为了执行这样的操作,流程引擎提供了流程实例重启 API,该 API 是通过RuntimeService.restartProcessInstances(...)实现。此 API 允许使用 Fluent 构建器在一次调用中指定多个实例化指令。
这些操作也可通过 REST 使用:重新启动流程实例和重新启动流程实例(异步),API接口地址https://docs.camunda.org/rest/camunda-bpm-platform/7.19/#tag/Process-Definition/operation/restartProcessInstance
云程低代码开发平台对Camunda开源流程引擎进行了扩展,实现了“流程激活/流程重启”,无需编写代码,通过配置即可完成。
工作流体验地址:http://www.yunchengxc.com
1、流程实例重启示例
例如,以下流程模型,其中红点标记活动任务:
让我们假设流程实例已被工作人员使用以下代码在外部取消:
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().singleResult();
runtimeService.deleteProcessInstance(processInstance.getId(), "any reason");
之后,管理员决定恢复该流程实例的上一个状态。
runtimeService.restartProcessInstance(processInstance.getProcessDefinitionId())
.startBeforeActivity("receivePayment")
.startBeforeActivity("shipOrder")
.processInstanceIds(processInstance.getId())
.execute();
已使用最后一组变量重新启动流程实例。但是,在重新启动的流程实例中只设置全局变量。本地变量可以手动设置,例如通过调用RuntimeService.setVariableLocal(…)。注意:历史流程实例和重新启动的流程实例的 ID 不同。
2、操作语义
在下文中,记录了流程实例重新启动功能的确切语义。建议阅读本节以充分了解此功能的效果、功能和局限性。
2.1、实例化指令类型
Fluent 流程实例重启生成器说明:
- startBeforeActivity(String activityId)
- startAfterActivity(String activityId)
- startTransition(String transitionId)
有关指令类型的信息,请参阅修改指令类型部分https://docs.camunda.org/manual/7.19/user-guide/process-engine/process-instance-modification/#modification-instruction-types。
2.2、选择要重新启动的流程实例
可以通过提供一组流程实例ID或提供历史流程实例查询来选择流程实例以重新启动。还可以同时指定进程实例ID列表和查询。然后,要重新启动的流程实例将是结果集的并集。
2.2.1、流程实例列表
应该重新启动的流程实例可以指定为流程实例ID的列表:
ProcessDefinition processDefinition = ...;
List<String> processInstanceIds = ...;
runtimeService.restartProcessInstances(processDefinition.getId())
.startBeforeActivity("activity")
.processInstanceIds(processInstanceIds)
.execute();
或者,对于静态数量的流程实例,有一个方便的 varargs 方法:
ProcessDefinition processDefinition = ...;
HistoricProcessInstance instance1 = ...;
HistoricProcessInstance instance2 = ...;
runtimeService.restartProcessInstances(processDefinition.getId())
.startBeforeActivity("activity")
.processInstanceIds(instance1.getId(), instance2.getId())
.execute();
2.2.2、历史流程实例查询
如果事先不知道这些实例,则可以通过历史流程实例查询来选择流程实例:
HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService
.createHistoricProcessInstanceQuery()
.processDefinitionId(processDefinition.getId())
.finished();
runtimeService.restartProcessInstances(processDefinition.getId())
.startBeforeActivity("activity")
.historicProcessInstanceQuery(historicProcessInstanceQuery)
.execute();
2.3、跳过侦听器和输入/输出映射
可以跳过执行和任务侦听器的调用,以及执行重新启动的事务的输入/输出映射。当在无法访问相关流程应用程序部署及其包含的类的系统上执行重新启动时,这可能非常有用。
在API中,两种方法#skipCustomListeners和#skipIoMappings可用于此目的:
ProcessDefinition processDefinition = ...;
List<String> processInstanceIds = ...;
runtimeService.restartProcessInstances(processDefinition.getId())
.startBeforeActivity("activity")
.processInstanceIds(processInstanceIds)
.skipCustomListeners()
.skipIoMappings()
.execute();
2.4、使用初始变量集重新启动流程实例
默认情况下,使用最后一组变量重新启动流程实例。要选择初始变量集,请使用initialSetOfVariables方法。
此功能不仅复制开始变量,还将复制在旧流程实例的开始活动中设置的所有流程变量的第一个版本。
ProcessDefinition processDefinition = ...;
List<String> processInstanceIds = ...;
runtimeService.restartProcessInstances(processDefinition.getId())
.startBeforeActivity("activity")
.processInstanceIds(processInstanceIds)
.initialSetOfVariables()
.execute();
如果历史流程实例没有唯一的启动活动,则无法设置初始变量集。在这种情况下,不会接管任何变量。
2.5、省略历史流程实例的Business Key
默认情况下,流程实例将使用与历史流程实例相同的business key重新启动。通过使用withoutBusinessKey方法,不会设置重新启动的流程实例的business key。
ProcessDefinition processDefinition = ...;
List<String> processInstanceIds = ...;
runtimeService.restartProcessInstances(processDefinition.getId())
.startBeforeActivity("activity")
.processInstanceIds(processInstanceIds)
.withoutBusinessKey()
.execute();
2.6、执行
重新启动可以同步执行(阻塞)或异步执行 (非阻塞)通过使用批处理。
以下是选择其中之一的一些原因:
在以下情况下使用同步执行:
- 流程实例数较少
- 重新启动应该是原子性的,即应该立即执行,如果至少有一个进程实例无法重新启动,则应该失败
在以下情况下使用异步执行:
- 流程实例数量较多
- 所有流程实例都应该与其他实例解耦地重新启动,即每个实例都在自己的事务中重新启动
- 重新启动应该由另一个线程执行,即作业执行器应该处理执行
2.6.1、同步执行
若要同步执行重新启动,请使用execute方法。它将阻塞,直到重新启动完成。
ProcessDefinition processDefinition = ...;
List<String> processInstanceIds = ...;
runtimeService.restartProcessInstances(processDefinition.getId())
.startBeforeActivity("activity")
.processInstanceIds(processInstanceIds)
.execute();
如果所有流程实例都可以重新启动,则重新启动成功。
2.6.2、异步批处理执行
要异步执行重新启动,请使用executeSync方法。它将立即返回,并引用执行重新启动的批处理。
ProcessDefinition processDefinition = ...;
List<String> processInstanceIds = ...;
Batch batch = runtimeService.restartProcessInstances(processDefinition.getId())
.startBeforeActivity("activity")
.processInstanceIds(processInstanceIds)
.executeAsync();
使用批处理,流程实例重新启动被拆分为几个异步执行的作业。这些批处理作业由作业执行器执行。如果所有批处理执行作业都已成功完成,则批处理即告完成。然而,与同步执行相比,不能保证所有或不重新启动流程实例。由于重新启动被拆分为几个独立的作业,每个作业都可能失败或成功。
如果重新启动作业失败,则作业执行器会重试该作业,如果没有剩余重试次数,则会创建事件。在这种情况下,需要手动操作才能完成批量重新启动:可以增加作业的重试次数,也可以删除作业。删除将取消特定实例的重新启动,但不会影响超出此范围的批处理。