Activiti7实战:从数据库表变化逆向解析会签与或签的运行机制
当你在深夜接到告警电话,线上审批流程卡在某个会签节点已经两小时——作为团队里最熟悉Activiti7的开发者,此刻需要的不是官方文档的标准答案,而是直指问题本质的排查手段。本文将带你像法医解剖现场一样,通过分析act_ru_task、act_hi_taskinst等核心表的字段变化,逆向推演出多实例任务的真实运行状态。
1. 解剖Activiti7的多实例任务模型
工作流引擎最精妙的设计在于:所有运行时状态都忠实地反映在数据库表中。理解下面三个核心表的协作关系,就像掌握了流程引擎的X光透视能力:
act_ru_task:运行时任务表,相当于流程的"短期记忆"act_hi_taskinst:历史任务表,记录完整的"生命周期病历"act_ru_execution:运行时执行流,揭示流程的"神经传导路径"
多实例任务(会签/或签)的本质是同一个节点生成多个并行或串行的任务实例。当我们在流程定义中设置multiInstanceLoopCharacteristics时,引擎会在运行时创建一组具有特殊标记的任务:
-- 典型的多实例任务查询 SELECT * FROM act_ru_task WHERE PROC_INST_ID_ = '流程实例ID' AND IS_COUNT_ENABLED_ = 1;关键字段IS_COUNT_ENABLED_=1就是多实例任务的身份证。更精妙的是,每个子任务会共享相同的PROC_INST_ID_但拥有不同的EXECUTION_ID_,这解释了为什么同一个会签节点会产生多条任务记录。
2. 会签场景的数据库指纹分析
假设我们有一个需要三人会签的采购审批流程,完成条件设置为"超过半数同意即通过"。当第二个审批人完成任务时,数据库会留下这样的痕迹:
2.1 运行时表(act_ru_task)的生死簿
| 任务ID | 执行ID | 任务名称 | 处理人 | 创建时间 | 是否多实例 |
|---|---|---|---|---|---|
| 5020 | 5021 | 采购审批 | 张三 | 09:00 | 1 |
| 5023 | 5024 | 采购审批 | 李四 | 09:05 | 1 |
| 5026 | 5027 | 采购审批 | 王五 | 09:10 | 1 |
当李四完成任务后,神奇的事情发生了:
- 所有相关任务瞬间消失:引擎会原子性地删除本节点所有运行时任务
- 历史表记录分化:通过
delete_reason_字段揭示不同命运
2.2 历史表(act_hi_taskinst)的考古现场
SELECT TASK_DEF_KEY_, ASSIGNEE_, END_TIME_, DELETE_REASON_ FROM act_hi_taskinst WHERE PROC_INST_ID_ = '流程实例ID' ORDER BY START_TIME_;查询结果会呈现戏剧性的对比:
| 任务定义KEY | 处理人 | 结束时间 | 删除原因 |
|---|---|---|---|
| approve | 张三 | 09:30 | completed |
| approve | 李四 | 09:35 | NULL |
| approve | 王五 | NULL | multi-instance completion condition met |
这三个字段组合就是诊断会签问题的"三原色":
- END_TIME_为NULL:任务被强制终止,未实际完成
- DELETE_REASON_包含'multi-instance':因完成条件满足而被系统清理
- NULL删除原因:实际完成的任务(本例中李四是第二个审批人,触发完成条件)
3. 或签异常的场景还原术
或签与会签的最大区别在于完成条件的严格程度。当遇到"明明有人审批了流程却不推进"的灵异事件时,按这个检查清单排查:
验证完成条件表达式
-- 查询流程变量验证实际值 SELECT * FROM act_ru_variable WHERE EXECUTION_ID_ IN ( SELECT ID_ FROM act_ru_execution WHERE PROC_INST_ID_ = '流程实例ID' );重点检查
nrOfCompletedInstances是否≥1检查历史任务终止原因
-- 筛选异常终止记录 SELECT * FROM act_hi_taskinst WHERE PROC_INST_ID_ = '流程实例ID' AND DELETE_REASON_ LIKE '%exception%';执行流卡点分析
-- 定位阻塞的执行流 SELECT * FROM act_ru_execution WHERE PROC_INST_ID_ = '流程实例ID' AND IS_ACTIVE_ = 1 AND ACT_ID_ = '或签节点ID';
我曾遇到过一个经典案例:审批人完成任务后,流程仍然停滞。最终发现是Completion Condition表达式被误写为${nrOfCompletedInstances == 1}(会签写法)而非${nrOfCompletedInstances >= 1}(或签标准)。这个错误导致必须恰好一人审批才能通过,与或签的设计初衷背道而驰。
4. 高级调试技巧与性能优化
当流程实例量达到百万级时,直接查询生产数据库可能引发性能问题。这里分享几个实战验证过的技巧:
4.1 安全高效的查询方案
-- 使用覆盖索引优化查询 EXPLAIN SELECT ID_, NAME_, ASSIGNEE_, CREATE_TIME_ FROM act_hi_taskinst FORCE INDEX (ACT_IDX_HI_TASK_INST_PROCINST) WHERE PROC_INST_ID_ = '流程实例ID' LIMIT 100;关键点:
- 强制使用
ACT_IDX_HI_TASK_INST_PROCINST索引 - 只选择必要字段减少I/O
- 添加LIMIT防止结果集过大
4.2 变量注入监控策略
在多实例任务中,变量传递异常是常见故障点。这个监控脚本可以实时捕获变量变化:
// 注册执行监听器捕获变量事件 runtimeService.addEventListener(new ExecutionListener() { @Override public void notify(DelegateExecution execution) { if("multiInstanceBody".equals(execution.getCurrentActivityId())) { logger.info("多实例变量快照: {}", execution.getVariables().entrySet().stream() .filter(e -> e.getKey().startsWith("nrOf")) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); } } }, ExecutionListener.EVENTNAME_TAKE);4.3 历史数据归档策略
对于运行多年的系统,建议定期归档历史数据:
-- 创建历史数据归档表 CREATE TABLE act_hi_taskinst_archive LIKE act_hi_taskinst; -- 分批迁移数据 INSERT INTO act_hi_taskinst_archive SELECT * FROM act_hi_taskinst WHERE END_TIME_ < DATE_SUB(NOW(), INTERVAL 3 MONTH) LIMIT 10000;配合Spring Batch可以实现无停机迁移。记得先迁移数据再创建索引,速度能提升5-8倍。