1. 从裸用 Activiti 到建设流程平台:一次架构演进复盘
先说背景。我过去几年带团队做过不少企业内部系统的流程模块,早期项目图省事,直接在业务代码里嵌 Activiti,部署一个流程就塞一个流程定义,所有审批逻辑都往引擎里写。前两年还好,等流程数量一多、参与方一杂,问题就全冒出来了:流程定义散落在各个服务里、发起和审批入口五花八门、改一条流程要发版上线、跑挂一个节点连日志都找不到。到了后面,光靠 Activiti 的 API 已经撑不住整个企业的流程诉求了。
这篇博文就围绕一条主线:怎么从“业务系统里用 Activiti”过渡到“统一的企业流程平台”。我会先复盘裸用阶段踩过的坑,再讲平台化改造时的架构设计、核心模块拆分、几个关键落地细节(包括 Activiti 数据库版本选型、离线插件安装这类很具体的问题),最后把线上实施过程中遇到的典型故障和排查思路整理成一份速查表。
如果你现在正处在“流程代码越写越多、却感觉越来越难维护”的阶段,这篇文章应该能帮你看清楚问题出在哪,以及从哪儿下手去做升级。
2. 为什么非要从 Activiti 走向流程平台
2.1 裸用 Activiti 的典型痛点
先说场景。大部分团队第一次接触 Activiti,都是因为业务里需要审批流。最常见的做法是在订单、报销、合同这类应用里引入 Activiti 依赖,然后自己封装一层 Service,谁要发起流程就调用一下,谁要审批就查一下任务列表。前期只有两三条流程的时候,这套玩法效率很高,因为 Activiti 对 BPMN 2.0 规范的支持非常完整,流程定义管理、任务分配、流程变量、历史数据它全都帮你做掉了。
但随着流程数量增长,几个问题会越来越明显。
第一,流程定义和业务系统强耦合。每条流程的 bpmn 文件放在各自工程里,改了要跟着业务服务一起打包发版。哪天产品提了一句话说“合同审批的第三级审批人换成部门总监”,本意是个五分钟的配置改动,结果你愣是得走一次发布流程。第二,执行引擎分散。好几个服务各自引入 Activiti,等于跑了好几套流程引擎,每个引擎维护自己的 ACT_* 表,跨系统的流程协同根本做不了。第三,监控和排查能力约等于零。流程实例卡在哪个节点?哪个环节耗时最长?有没有异常重试机制?这些在裸用阶段基本靠手工翻数据库。
2.2 流程平台要解决什么问题
所以做流程平台,本质上不是“换掉 Activiti”,而是“把 Activiti 收编到平台内部”,让业务系统不再各自为战。这里面的核心转变有三个。
一是角色转变。原来 Activiti 是嵌在业务代码里的一个组件,改造后它变成了平台层的基础引擎,对外提供统一的服务能力。业务系统不再直接触碰 engine API,而是走平台提供的接口。
二是能力补齐。Activiti 本身解决的是流程执行问题,但它不关心流程怎么设计、怎么管理、怎么监控、怎么统计。流程平台要在引擎外面补上这些能力:可视化流程设计器、统一流程管理后台、流程实例监控、超时提醒、SLA 统计、组织权限同步等等。
三是标准统一。所有业务流程都通过平台发起和流转,流程定义统一管理,任务统一处理,接口统一规范。这样不管是新的审批需求还是对接外部系统,都有了一套标准的接入方式,而不是每个项目自己造一套轮子。
3. 流程平台的整体架构设计与能力规划
3.1 平台分层架构怎么定
在做架构设计的时候,我没有一上来就写代码,而是先定了一个原则:平台要跟业务系统解耦,但也要让业务系统接入得足够轻。最后落地下来的分层结构大致是这样的。
接入层对外提供统一的 REST API 和消息通道,主要解决身份认证、参数校验、接口鉴权这些通用问题。不管是内部系统还是外部系统,接入流程平台都走这一层,不允许有人绕过平台直接调引擎。服务层是平台的业务核心,流程定义管理、流程实例管理、任务管理、委派转办、驳回跳转、会签票签这些能力都在这一层封装,对上屏蔽引擎细节。引擎层就是 Activiti,负责 BPMN 解析、流程驱动、任务分配、事件触发这些标准能力。管理端是运营和配置人员的入口,负责流程设计、部署、权限配置、监控告警。存储层除了 Activiti 自身的 ACT_* 表,还增加了平台自己的业务表,用来存流程分类、表单绑定关系、操作日志、流程与业务单据的关联关系。
这个分层结构其实参考了业界做流程平台比较通用的模式。重点不在架构图本身多好看,而在“边界要清晰”:引擎就是引擎,平台就是平台,业务系统就是业务系统,谁也别跨界。
3.2 核心模块拆解
平台化改造过程中,我觉得有五个模块是必须认真做的,缺一个后面都难受。
流程设计器。设计器是给流程管理员用的,不是给程序员用的。所以不能直接丢个 Activiti Modeler 给业务人员,那样他们光画网关和事件就会懵。我们的做法是基于 bpmn-js 做了一套简化版设计器,默认只暴露开始事件、用户任务、排他网关、结束事件这几种常用要素,其他高级元素在高级模式下才展示。设计器最终生成标准的 BPMN 2.0 XML,保存到流程定义库。
流程定义管理。这个模块管的是流程的“版本、分类、状态、权限”。部署新版本的时候,设置为待发布状态,确认没问题再激活。已经发起的老流程继续使用旧版本,新发起默认走新版本,这是 Activiti 本身就支持的版本机制,平台要做的就是把它合理地暴露出来。
统一任务中心。每个业务系统都有一套自己的待办列表,这是常态。平台要做的不是取代业务系统的待办,而是提供统一的任务查询和操作接口。待办数据通过消息实时同步给业务系统,业务侧只需要接收数据做展示,真正的审批动作统一回写平台。
实例监控与运维。这是裸用 Activiti 时最缺的能力。平台要能实时看到每个流程实例走到哪个节点、当前处理人是谁、这个节点停留多久、有没有超时告警。Activiti 的引擎事件监听器就是做这件事的抓手,通过监听任务创建、任务完成、流程结束这些事件,把数据加工后写入平台的监控表,再配合定时任务做超时检测。
组织与权限同步。企业流程离不开组织架构,但 Activiti 自带的 ACT_ID_* 身份体系一般不建议直接用。原因很简单:企业内部的组织架构基本上都在统一的权限系统里维护,流程平台要是自己再维护一套身份数据,很快就会出现“人已经离职了,流程还在给他审批”这种尴尬事。我们的方案是平台只保存一份与上游同步过来的用户、部门、角色的简化副本,每次同步做全量比对+增量更新,审批人的解析统一通过同步数据完成。
3.3 方案选型:为什么继续用 Activiti,没有换成别的引擎
这块我多说几句。改造过程中有不少同事提过,要不要干脆换掉 Activiti,用 Flowable、Camunda 或者自研一套。我的建议是:不要轻易换引擎。
原因有三点。第一,Activiti 的成熟度和社区基础在 Java 技术栈里仍然很能打,BPMN 2.0 的完整支持、丰富的 API、数据库表结构设计都很稳定,团队里很多开发对它已经足够熟悉。第二,流程平台的核心竞争力根本不在引擎本身,而在引擎之上封装的产品能力、集成能力和运维能力。换一个引擎不会让你的平台更好用,反而要承担巨大的迁移和兼容成本。第三,Activiti 7 的版本在扩展性和云原生适配方面都有了改进,够用。
所以我的建议是:引擎层面稳定优先,不要为了“技术新”去冒风险,平台的价值要靠上层能力和工程治理来体现。
4. 实操关键:版本、数据库、离线插件与二次封装
4.1 Activiti 数据库版本选型与表结构差异
Activiti 的数据库版本问题是很多团队容易忽略的重灾区。Activiti 5.x、6.x、7.x 的 ACT_* 表结构有差异,尤其涉及到历史数据和流程实例的平滑过渡,绝不能拍脑袋升级。
我这里列一个最简单的对照表。
| 版本 | 主要变化 | 升级注意点 |
|---|---|---|
| 5.x | 经典的 ACT_RE_/RU_/HI_/ID_ 结构 | 早期版本大量项目使用,升级需脚本迁移 |
| 6.x | 表结构微调、JSON 支持增强 | 与 5.x 不兼容,需要完整迁移流程定义和历史数据 |
| 7.x | 模块化重构,支持 Spring Boot 2.x | API 变化明显,依赖引入方式变化大 |
我们平台最终选的是 7.x 线。原因很简单:项目技术栈是 Spring Boot 2.x,Activiti 7 的 starter 集成最顺畅,而且模块化做得比 6.x 清晰,适合在这之上做二次开发。
但这里有个很关键的坑:Activiti 的自动建表机制在生产环境要关掉。在 application.yml 里配置:
spring: activiti: database-schema-update: false db-history-used: true history-level: audit生产环境强烈建议显式关闭 schema 自动更新,由 DBA 审核脚本后手动执行。历史级别按需选择:如果流程不需要精细化追溯,用 audit 就够了,full 级别会记录所有变量变化,数据量增长非常快。
4.2 关于 Activiti 数据库表的核心理解
聊数据库版本,有个问题绕不开:Activiti 启动后自动生成的那几十张表都是干什么的?如果这一层搞不清楚,后面排查问题会非常被动。
我通常把 Activiti 的表分成四类。资源与定义类,主要是 ACT_RE_ 前缀,比如 ACT_RE_PROCDEF 存流程定义信息,ACT_RE_DEPLOYMENT 存部署记录。运行时数据类,主要是 ACT_RU_ 前缀,像 ACT_RU_EXECUTION 存执行实例信息,ACT_RU_TASK 存当前待办任务,ACT_RU_VARIABLE 存流程变量,这类表是流程引擎运行时的核心,数据会随着流程结束被清理。历史数据类,主要是 ACT_HI_ 前缀,流程实例历史、任务历史、活动历史、变量历史都在这里,数据只会增加不会减少,是需要重点做数据治理的部分。通用数据类,主要是 ACT_GE_ 前缀,像 ACT_GE_BYTEARRAY 存 BPMN 文件的二进制内容,ACT_GE_PROPERTY 存引擎版本和 ID 生成相关的属性。
理解这四类表的生命周期差异很重要:运行时表要关注性能,避免大量积压;历史表要关注容量,定期归档和清理。很多团队流程跑一段时间数据库就变慢了,十有八九是历史表膨胀导致的。
4.3 IDEA Activiti 插件离线安装的实践
再说一个开发期很实际的问题:IDEA 里 Activiti 插件离线安装。很多企业内部开发环境是不能访问公网插件市场的,但画 BPMN 文件又确实需要可视化工具。
这里分享一个可靠的离线安装路径。第一步,找一台能上网的机器,到 IDEA 插件市场下载对应版本插件包。以 Activiti BPMN visualizer 为例,在插件市场页面选择与你 IDEA 版本兼容的版本号,下载 .zip 格式插件包。第二步,把插件包拷贝到内网开发机。第三步,打开 IDEA 的 Settings,进入 Plugins,点击齿轮图标,选择 Install Plugin from Disk,选中插件包后重启 IDEA。
需要特别留意的是,插件版本跟 IDEA 版本有严格的兼容性,装完后右下角提示插件不兼容,多半是版本选错了。另外离线安装虽然省去了网络问题,但插件自身的依赖不会自动补全,所以尽量选择功能完整、无额外运行时依赖的插件。如果你只是需要一个轻量的查看器,用 IntelliJ 自带的 Diagram 也能临时顶一顶,但编辑 BPMN 还是用专门的 Activiti 插件效率高。
4.4 对 Activiti 引擎做二次封装
平台层对 Activiti 的封装,我建议把握一个度:不是所有的引擎 API 都要暴露出去,而是只暴露业务真正关心的那几种能力。统一的流程发起接口、统一的审批操作接口、统一的流程查询接口,这三类是最基本的。
举个例子,我们封装发起流程的接口时,参数里包含流程定义 Key、业务单据 ID、发起人、审批变量。平台内部做这几件事:根据定义 Key 获取最新版本定义、创建流程实例并绑定业务 Key、记录业务系统与流程实例的关联关系、初始化流程变量。
审批操作同样如此。对外只提供一个 complete 接口,内部根据当前任务节点类型做不同处理:普通用户任务直接 complete;会签任务处理票签逻辑;网关节点自动流转。这样对业务系统来说,所有审批都只有“提交”一个动作,复杂度全被平台吸收了。
封装的时候还要特别注意事务边界。Activiti 的操作跟业务系统的事务不在一个事务上下文里,如果平台只管调引擎,不考虑业务数据的一致性,就会出现“业务单子成功了但流程没发起”或者反过来“流程走了一半业务数据没存上”的情况。我们的方案是提供事务模板接口,业务系统在同一个事务里先写业务数据、再调用流程接口,平台内部通过同步事务状态来保证数据一致。
5. 实施过程中的坑与排查实录
5.1 常见问题速查表
这部分我整理了一份速查表,都是从实际项目里总结出来的,比看文档印象深得多。
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 流程实例没有按预期流转 | 排他网关条件未覆盖所有分支 | 检查 BPMN 条件表达式与流程变量类型 |
| 待办任务突然消失 | 任务被其他节点串签/会签处理 | 查看历史任务表,追踪任务生命周期 |
| 引擎启动报表不存在错误 | 关闭了自动建表但没有初始化脚本 | 手动执行对应版本的 create 脚本 |
| 历史表数据增长很快 | history-level 设置过高 | 调整级别,增加定期归档任务 |
| 发起流程时接口超时 | 流程实例数过多导致引擎性能下降 | 检查运行时表数据量、索引命中情况 |
| 同一个流程实例重复发起 | 业务系统未做幂等处理 | 增加唯一业务 Key 与事务控制 |
5.2 几个印象深刻的线上案例
我挑两个亲历过的问题展开讲讲。
第一个是流程不会自动流转的问题。现象是某条合同审批流在第一个审批节点通过之后,第二个节点一直没有生成待办。我查了流程实例运行表,发现执行实例确实停留在第一个网关前。后来逐行看 BPMN XML,发现问题出在排他网关的条件表达式上。流程变量是一个 Integer,但 BPMN 条件里写的是字符串比较,类型不匹配导致条件永远为 false,网关没有出口可选,流程自然就卡住了。解决方式很简单,统一条件表达式的类型转换规则。但这之后我在代码审查里加了一条硬性要求:流程条件里的变量类型必须在流程启动时强制确认,不允许隐式转换。
第二个是历史数据膨胀引发的性能问题。上线三个月后,有业务系统反馈流程发起接口越来越慢。查了一圈发现不是引擎本身的问题,而是 ACT_HI_VARINST 表已经积压了一千多万行,联合查询性能急剧下降。后来我们做了一套完整的归档方案,每天定时把三个月前的历史数据迁移到归档库,业务查询走归档库,在线库只保留热数据。这个例子想说明的是:用 Activiti 做流程平台,数据库数据治理一定要前置设计,不要等到线上出问题了才回头补。
5.3 事务一致性和性能调优的两个经验
再讲两个容易被忽视的细节。
事务一致性是流程平台必须正视的问题。Activiti 内部操作有自己的事务管理,如果业务系统在同一个流程操作里还要更新自己的业务表,就涉及到跨数据源事务,不能简单依赖 Spring 默认的本地事务。我推荐的做法是尽量保证“一个操作只写一个数据源”,要么先写业务数据、成功后通过异步消息触发流程,要么先发起流程、在流程回调里写业务数据。如果必须要同步强一致,再考虑引入分布式事务方案,但尽量避免,因为复杂度太高。
性能调优方面,Activiti 7 的默认配置在大部分场景下是够用的,但如果流程实例并发量很高,有几个参数值得去调。异步执行器的核心线程数、最大线程数和队列容量,决定了引擎处理异步事件的能力;历史数据清理任务建议放到业务低峰期执行,避免占用数据库资源。另外,所有高频查询接口都要注意运行时表和历史表的索引,索引缺失在数据量不大的时候没什么感觉,数据量一上来就是灾难。
6. 平台上线后的运维治理与演进方向
6.1 监控指标与告警体系
流程平台上线的第一天,就要把监控体系铺起来。我的经验是分三个维度来做。
第一个维度是引擎健康度。Activiti 引擎自身能不能正常工作,包括引擎启动时间、异步执行器队列积压量、流程引擎数据库连接池使用率。第二个维度是流程运行指标。按流程定义维度聚合,统计发起量、完成量、平均耗时、超时率。这些指标能从数据上反映哪条流程需要优化,比如某条流程的平均耗时明显偏高,那大概率是审批环节配置不合理或者有节点处理人缺失。第三个维度是业务异常监控。包括任务创建失败、流程实例异常结束、事件监听处理失败。
告警规则不用一开始就设得很复杂,先把最核心的几条配上:流程实例异常终止、异步任务积压超过阈值、任务停留超过 SLA、数据库连接池耗尽。告警渠道最好能接入企业统一告警中心,不要自己单独搞一套。
6.2 从流程平台到流程体系的演进
平台稳定运行一段时间之后,可以开始思考更高一层的事情。我比较看好的方向有两个。
一个是流程分析与优化。平台积累了大量的流程运行数据,可以从数据里去发现流程瓶颈。比如报销流程平均要走五级审批,但数据显示超过一半的审批节点耗时不到一分钟,说明这些节点只是走个形式,完全可以精简掉。这就是数据驱动流程优化的典型场景。
另一个是流程资产化。当流程平台覆盖了企业核心业务流程之后,流程本身就成了企业的数字化资产。新业务系统上线时,不用再从零开始设计流程,而是从平台已有的流程资产库里选择、组合、复用。这个阶段,平台的价值就不只是“跑流程”了,而是上升到了企业运营基础设施的层面。
7. 最后的一些心里话
我个人在实际项目中最大的一个体会是:流程平台不是一个一次性的技术项目,它更像是一个随着企业业务演化而持续生长的底座。建好一套流程架构很容易,难的是让团队真正愿意把流程都沉淀到平台上来,这背后涉及组织协同、权限梳理、历史系统迁移,比技术本身难得多。
另外还想提醒一点,无论你做得多完善,永远会给后续的迭代留出空间。Activiti 的版本会持续更新,平台的接入规范也会不断调整,一开始就把架构做死了,后面反而寸步难行。保留核心抽象灵活性,让新流程接入的成本尽可能低,才是这套架构能长期走下去的关键。
如果这篇文章里的经验能让你在规划企业流程架构升级时少走几个弯路,那就值了。