Flowable:BPMN 流程如何从模型走到运行时
Flowable 是一组可嵌入 Java 应用的流程引擎。BPMN 引擎负责可预先建模的业务流程,CMMN 引擎处理更依赖事件和人工判断的案例,DMN 引擎执行决策表;Event Registry、身份、任务、变量和作业服务提供跨引擎共享能力。应用既可以直接调用 Java API,也可以把引擎放在服务端,通过 REST 接入。
从源码看,BPMN 图不会被逐节点解释成一串递归方法调用。部署阶段先把 XML 转成模型,为节点挂接 ActivityBehavior;运行阶段再把一次 API 调用包装成 Command,在同一个 CommandContext 中不断消费 Agenda 队列里的 operation。流程走到 User Task、Receive Task 或异步节点时,内存调用停止,执行位置被写入数据库,下一次外部事件或作业线程再恢复执行。
这套结构同时解释了 Flowable 的优势和成本。服务 API、命令拦截器、持久化 Session、流程行为与作业服务彼此分层,扩展点很多;另一方面,完整仓库包含 90 个 flowable-* 模块,配置项、共享服务和兼容层也很多。表达式、脚本、Java Delegate、HTTP、Mail 等节点还可能执行带副作用的业务逻辑,生产环境必须把模型发布权、可调用 Bean、网络出口和凭据管理当作安全边界。
下面先画出全仓库的职责地图,再以 BPMN 主引擎为中心,沿 Spring Boot 启动、模型部署、流程启动、人工任务完成和异步作业五条链路深入。CMMN、DMN、Event Registry 和 Flowable 5 兼容模块只说明各自如何接入,不声称逐一覆盖全部模块。
GitHub 仓库信息
| 信息 | 内容 |
|---|---|
| 项目标题 | Flowable |
| 项目描述 | 面向开发者、系统管理员和业务用户的轻量工作流与业务流程管理平台,提供 Java 编写的 BPMN 流程、CMMN 案例和 DMN 决策引擎。 |
| GitHub 仓库 | flowable/flowable-engine |
| 官网地址 | Flowable 官网 |
| 主要开发语言 | GitHub Languages API 的动态元数据快照(2026-09-04,不能绑定 source_commit):Java 94.3%、JavaScript 4.95%、SQLPL 0.21% |
| 开源许可证 | Apache-2.0 |
| 最近代码更新 | 2026-09-02(GitHub pushed_at,核对日期:2026-09-04) |
仓库结构与模块职责
根 POM 默认聚合模型、共享服务和核心引擎,deploy、distro 等 Maven Profile 再引入 Spring、REST、兼容层和集成模块。下面的目录树按语义裁剪,不等于完整模块清单。
flowable-engine/
├── modules/
│ ├── flowable-engine-common-api/ # 通用 Engine、Command、事件与异步接口
│ ├── flowable-engine-common/ # CommandContext、拦截器、Session、锁与 MyBatis 基础
│ ├── flowable-bpmn-model/ # BPMN 内存模型
│ ├── flowable-bpmn-converter/ # BPMN XML 与模型转换
│ ├── flowable-process-validation/ # BPMN 语义校验
│ ├── flowable-engine/ # BPMN API、命令、Agenda、行为、实体与查询
│ ├── flowable-task-service[-api]/ # 跨引擎任务实体和持久化服务
│ ├── flowable-variable-service[-api]/ # 变量类型、实体和持久化服务
│ ├── flowable-job-service[-api]/ # 异步、定时、暂停与死信作业
│ ├── flowable-eventsubscription-* # 消息、信号等事件订阅
│ ├── flowable-cmmn-* # CMMN 模型、转换、校验与案例引擎
│ ├── flowable-dmn-* # DMN 模型、XML 转换与决策引擎
│ ├── flowable-event-registry-* # 外部事件模型、通道和引擎集成
│ ├── flowable-spring/ # Spring 事务、生命周期与资源自动部署
│ ├── flowable-spring-boot/ # Starter、自动配置和样例
│ ├── flowable-rest/ # BPMN REST 资源
│ └── flowable5-* # 历史流程定义兼容路径
├── docs/docusaurus/docs/ # 官方文档源码
├── distro/sql/ # 多数据库建表与升级脚本
├── tooling/archetypes/ # 测试工程脚手架
├── build-tools/ # Checkstyle 等构建规则
└── pom.xml # 8.1.0-SNAPSHOT、JDK 17、模块与 Profile| 语义层 | 关键模块或文件 | 职责与上下游 |
|---|---|---|
| 公开 API | RepositoryService、RuntimeService、TaskService、ManagementService | 接收部署、启动、任务和作业操作,大多数实现立即转交给 CommandExecutor |
| 命令基础设施 | AbstractEngineConfiguration、CommandContext | 组装日志、事务、上下文和自定义拦截器,统一 Session flush、关闭和异常传播 |
| BPMN 部署 | flowable-bpmn-converter、flowable-process-validation、BpmnDeployer | 把 XML 解析为模型,执行 XSD/语义校验,持久化流程定义并刷新缓存和事件订阅 |
| BPMN 运行时 | ProcessInstanceHelper、FlowableEngineAgenda、ContinueProcessOperation | 建立执行树,计划 operation,执行节点行为并决定同步、等待或异步分流 |
| 共享状态服务 | flowable-task-service、flowable-variable-service、flowable-identitylink-service | 为 BPMN、CMMN 等引擎提供任务、变量和参与者关系的统一实体与持久化能力 |
| 后台作业 | flowable-job-service | 查询到期作业、写入锁 owner 和到期时间、提交线程池、执行 Handler、重试或转死信 |
| 宿主集成 | flowable-spring、flowable-spring-boot、flowable-rest | 将引擎放入 Spring 生命周期,发现流程资源,暴露 Bean 或 HTTP 入口 |
五个核心机制
API 最终汇入 Command
Command 是描述一次引擎操作及其参数的对象。RuntimeServiceImpl.startProcessInstanceByKey()、RepositoryServiceImpl.deploy() 和 TaskServiceImpl.complete() 的共同点,是把参数封装成命令后交给 CommandExecutor。默认拦截器链包含日志、数据库特定重试、事务、CommandContext 和最终命令执行器;前后还可以插入自定义 CommandInterceptor。
CommandContext 是一次引擎调用的工作单元。CommandContext 保存本次命令打开的 Session、异常和关闭监听器:正常结束先通知监听器,再 flush Session;失败时触发失败监听器;无论成功与否都关闭 Session。Spring 版本用 SpringTransactionInterceptor 接入宿主事务,嵌套服务调用还可以复用当前上下文。这意味着一个 API 返回成功,通常也意味着同一命令中的持久化刷新已经完成;运行到异步边界后的业务结果则不在这一事务承诺里。
部署把模型节点变成可执行行为
BPMN XML 先由 BpmnXMLConverter 转为 BpmnModel,再经过 ProcessValidator 做语义校验。解析处理器随后把 User Task、Service Task、Gateway、Event 等模型元素绑定到具体 ActivityBehavior。运行期因此无需重新解释 XML,只需从流程定义缓存取得模型,并调用当前节点已经挂接的行为。
部署不只是保存一份文件。BpmnDeployer 还会计算定义版本和 ID,写入授权,更新定时器和事件订阅,生成可选流程图,刷新流程定义缓存。重复部署过滤、租户、派生部署和 Flowable 5 兼容会在同一链路中改变行为。
Agenda 如何推进执行
每次根命令都有一个 Agenda,也就是当前命令的待执行 operation 队列。命令本身可以把 ContinueProcessOperation、TakeOutgoingSequenceFlowsOperation、TriggerExecutionOperation 等 operation 放入队列;CommandInvoker 持续取出并执行,直到队列为空。一个 operation 又能计划后续 operation,所以网关分支、子流程、边界事件和结束处理都能在同一事务内继续推进。
流程图的连线描述控制流,真正推进这些连线的是 Agenda 队列;execution tree 则保存流程实例、子流程和并行分支的运行位置。排查“为什么没有走到下一节点”时,应同时看当前 ExecutionEntity、当前 FlowElement、对应行为以及 Agenda 计划了什么,单看 Java 调用栈不够。
等待状态把控制权交回数据库
User Task 创建 TaskEntity 后不会主动计划离开当前节点。当前 execution 保留在该活动上,任务、变量和参与者关系写入 ACT_RU_* 表,调用结束。稍后 TaskService.complete() 删除或归档任务,计划 TriggerExecutionOperation,再由 UserTaskActivityBehavior.trigger() 离开节点。
Receive Task、消息捕获、定时器和外部事件也遵循类似原则。等待状态把执行位置持久化到数据库,当前线程随命令结束;JVM 可以在等待期间重启,另一个应用节点也可以根据同一数据库恢复流程。
异步作业把事务切成两段
当 FlowNode 标记为异步时,ContinueProcessOperation 不执行节点行为,而是创建 JobEntity 并调度。当前 API 事务只承诺作业已落库;后台获取线程再查询到期作业、设置锁 owner 与锁过期时间、放入执行线程池,并在新事务中调用 JobHandler。
异步节点把流程切成两个事务阶段:数据库作业先成为可靠交接点,线程池再作为后续消费者。集群节点通过锁字段和乐观锁竞争工作;失败作业在独立新事务中减少重试次数,进入定时作业等待下一次尝试,耗尽后进入死信表。
源码环境与运行边界
固定源码的根 POM 声明 8.1.0-SNAPSHOT 和 JDK 17。2026 年 9 月 4 日核对到的最新公开 Release 是 flowable-8.0.0,发布于 2026 年 2 月 27 日。Release 说明明确 Flowable 8 升级到 Spring Framework 7、Spring Boot 4 和 Jackson 3;快照源码仍保留 Jackson2JsonObjectRestVariableConverter 这条 Jackson 2 REST 兼容路径,不能把 8.1 快照行为直接等同于 8.0 Release。
本次完成了固定提交的静态阅读、根 POM 内容哈希比对和文档校验,没有运行目标仓库的 Maven 构建、JUnit、数据库、Spring Boot 应用、REST 服务或异步执行器。下面是复现路径,不是亲测记录。
获取同一份源码
前置条件是 Git、JDK 17 和可用的 Maven。选择空目录执行:
git clone https://github.com/flowable/flowable-engine.git
cd flowable-engine
git checkout d6d39ce1c69ff244f2d9dc6af756a9b95e865586
java -version
mvn -versiongit status --short --branch 应显示 detached HEAD 或该固定提交,并且没有意外改动。若只阅读 BPMN 主引擎,可先构建相关反应器模块:
mvn -pl modules/flowable-engine -am -DskipTests package预期成功信号是 Maven 以 BUILD SUCCESS 结束,并在相关模块生成 target/ 产物。此命令显式跳过测试;要验证测试,应另行运行测试目标并查看测试汇总:
mvn -pl modules/flowable-engine -am test核心 API 片段
官方入门文档使用独立 ProcessEngineConfiguration 配合 JDBC 数据库,也支持 Spring Boot Starter。Flowable 8 的 Spring 集成面向 Spring Boot 4.x。下面是围绕已配置好的三个 Service 的核心 API 片段,不是可单独编译的最小程序;数据库、引擎 Bean、BPMN 资源和 import 需要由宿主工程提供。最小验证不应停在应用启动,至少要完成部署、启动和人工任务续行:
Map<String, Object> variables = Map.of("days", 3);
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("processes/holiday-request.bpmn20.xml")
.deploy();
ProcessInstance instance = runtimeService
.startProcessInstanceByKey("holidayRequest", variables);
Task task = taskService.createTaskQuery()
.processInstanceId(instance.getId())
.singleResult();
taskService.complete(task.getId(), Map.of("approved", true));可观察结果应包括:部署查询能找到流程定义;启动后 ACT_RU_EXECUTION 有运行实例;走到 User Task 时任务查询返回一条任务;完成后任务消失并出现下一个等待状态或流程结束。若使用自动建表,开发环境可启用 schema update;生产环境更适合先审阅 distro/sql 或模块内数据库脚本并显式迁移。
整体架构地图
为了避免把 BPMN 深挖误读成“所有引擎都已逐链路展开”,各引擎在本文中的源码覆盖边界如下:
| 引擎或入口 | 固定源码入口 | 本文覆盖 | 未展开部分 |
|---|---|---|---|
| BPMN | ProcessEngineConfigurationImpl | 深入启动、部署、执行、人工任务和异步作业 | 全部 90 个语义模块的逐文件审计 |
| CMMN | CmmnEngineConfiguration | 引擎装配和共享任务、变量、作业服务边界 | plan item 生命周期和完整案例链路 |
| DMN | DmnEngineConfiguration | 决策引擎角色及与 BPMN 的集成位置 | 决策表解析和逐规则求值 |
| Event Registry | EventRegistryEngineConfiguration | 外部事件通道和共享服务边界 | 入站、出站通道的完整消息链路 |
| REST | ProcessInstanceCollectionResource | 作为 Java API 之上的传输适配器 | REST 资源逐接口分析、认证部署和权限配置 |
这张矩阵说明了一个重要边界:共享服务的存在可以从固定源码确认,但 CMMN、DMN 和 Event Registry 的完整业务语义仍需要各自的专门走读。
| 想理解的能力 | 阅读入口 | 继续向下读 |
|---|---|---|
| 引擎如何创建 | AbstractBuildableEngineConfiguration.buildEngine() | ProcessEngineConfigurationImpl.init()、ProcessEngineImpl |
| Spring Boot 如何装配 | ProcessEngineAutoConfiguration | ProcessEngineServicesAutoConfiguration、ProcessEngineFactoryBean、SpringProcessEngineConfiguration |
| API 的事务边界 | AbstractEngineConfiguration.initCommandInterceptors() | CommandContextInterceptor、TransactionContextInterceptor、CommandContext.close() |
| BPMN 如何部署 | RepositoryServiceImpl.createDeployment() | DeployCmd、BpmnDeployer、ParsedDeploymentBuilder、BpmnParse |
| 流程如何启动 | RuntimeServiceImpl.startProcessInstanceByKey() | StartProcessInstanceCmd、ProcessInstanceHelper、CommandInvoker |
| 人工任务如何恢复 | UserTaskActivityBehavior | CompleteTaskCmd、TaskHelper、TriggerExecutionOperation |
| 异步作业如何集群执行 | AcquireAsyncJobsDueRunnable | AcquireJobsCmd、ExecuteAsyncRunnable、JobRetryCmd |
| 动态跳转如何实现 | RuntimeService.createChangeActivityStateBuilder() | ChangeActivityStateBuilderImpl、ChangeActivityStateCmd、DefaultDynamicStateManager |
引擎启动链路
独立引擎
buildEngine() 只做四步:初始化配置、准备构建后回调、创建引擎、执行回调。真正复杂的是 ProcessEngineConfigurationImpl.init(),ProcessEngineConfigurationImpl.init() 按依赖顺序创建命令设施、表达式、数据源、服务、解析器、缓存、实体管理器、部署器、事件分发器和异步执行器。
ProcessEngineImpl 构造函数取得各项 Service,在命令事务中执行 schema management,注册引擎,并按配置启动已激活的异步执行器;buildEngine() 随后执行 post-build consumer。关闭时则先注销引擎、停止两个异步执行器,再关闭配置和通知生命周期监听器。
Spring Boot 引擎
Spring Boot 路线增加了两层宿主控制。ProcessEngineAutoConfiguration 创建 SpringProcessEngineConfiguration,注入 DataSource、事务管理器、异步执行器和自动部署资源;ProcessEngineServicesAutoConfiguration 再创建 ProcessEngineFactoryBean。FactoryBean 调用 buildProcessEngine(),并在销毁时关闭引擎。
SpringProcessEngineConfiguration 关闭了“引擎构造后立即启动执行器”的默认行为。Spring 生命周期进入 start() 后,start() 才遍历已构建引擎,调用 startExecutors() 并按 deployment mode 自动部署 classpath 中发现的流程资源;自动部署位置、后缀和检查开关由 FlowableProperties 暴露。这个顺序避免后台线程在 Spring 容器尚未完全就绪时抢先工作,也让 Flowable 的事务交给 PlatformTransactionManager 管理。
BPMN 部署链路
DeploymentBuilderImpl.deploy() 回到 RepositoryServiceImpl.deploy(),后者执行 DeployCmd。命令先处理 Flowable 5 兼容和重复部署过滤,再保存 DeploymentEntity,最后交给 DeploymentManager 中注册的 deployer。
ParsedDeploymentBuilder 只挑选 BPMN 资源。BpmnParse.execute() 依次执行 XML 转换、可选 XSD 校验、语义校验、parse handler 和图形信息处理;错误会中断整个命令。随后 BpmnDeployer.deploy() 检查 definition key 冲突,为新版本计算 ID,持久化定义和授权,更新定时器与事件,再刷新缓存。
部署阶段把“业务图”转换为“可执行定义”。若部署失败,运行时不会得到半成品模型,因为部署与定义写入处在同一个命令事务中。
流程实例启动链路
RuntimeServiceImpl.startProcessInstanceByKey() 创建 StartProcessInstanceCmd。命令按 key、tenant 和 fallback 规则解析流程定义,校验启动表单后交给 ProcessInstanceHelper。后者拒绝已挂起定义,从缓存取得 Process 和起始元素,创建根 execution、初始子 execution、变量、身份关系、历史记录与事件订阅,然后计划 ContinueProcessOperation。
CommandInvoker 的 while (!agenda.isEmpty()) 是同步推进的核心。ContinueProcessOperation 读取 execution 当前元素:遇到 Sequence Flow 就处理监听器和目标节点;遇到 FlowNode 就创建边界事件,选择同步、多实例或异步路径,再调用已经绑定的行为。
同步 Service Task 可能在一次 startProcessInstanceByKey() 中直接执行。若 Delegate 抛出普通运行时异常,当前命令事务回滚;若模型在前面设置了 async="true",启动事务只创建作业,Delegate 的异常由后台作业重试链处理。这是建模层直接改变事务边界的例子。
人工任务链路
创建任务并停在等待状态
UserTaskActivityBehavior.execute() 创建任务,解析名称、到期时间、优先级、表单、assignee、owner、候选用户和候选组。任务插入后才建立人员关系,因为 identity link 需要 task ID。创建监听器与 TASK_CREATED 事件在同一命令内触发。
如果 skip expression 判定跳过,任务会被立即删除并继续离开节点;普通 User Task 不调用 leave(),所以 Agenda 耗尽后事务结束,execution 留在当前节点。这正是可持久化等待状态。
完成任务并恢复流程
CompleteTaskCmd 先拒绝用 BPMN API 完成 CMMN 创建的任务,并保留 Flowable 5 兼容分流。TaskHelper.completeTask() 写入流程变量和本地变量,触发 complete listener 与事件,记录完成人和历史,再完成任务实体。若监听器抛出 BPMN Business Error,错误会按模型传播,不再执行普通续行;否则计划 TriggerExecutionOperation。
TriggerExecutionOperation 要求当前行为实现 TriggerableActivityBehavior。UserTaskActivityBehavior.trigger() 会检查同一 execution 下是否还有未删除任务,确认后才 leave()。这能防止绕过任务完成直接 signal execution。
异步作业链路
AcquireAsyncJobsDueRunnable 先看线程池剩余容量,再执行 AcquireJobsCmd。命令查询可执行作业,把本节点标识和锁到期时间写入每个作业。多个节点同时获取时,乐观锁异常被视为集群中的正常竞争;还可以选择全局获取锁,让节点轮流填充队列。
ExecuteAsyncRunnable 对 exclusive job 额外锁定流程实例,避免同一实例的并发作业交错修改执行树。事务型作业通过 ExecuteAsyncRunnableJobCmd 重新查询数据库,因为排队期间作业可能已被边界事件或另一分支删除;确认仍存在后才交给 JobManager 和具体 JobHandler。
失败路径不会在已回滚事务里直接修改重试次数。FailedJobListener.closeFailure() 使用 transactionRequiresNew() 创建新命令,BPMN 的 JobRetryCmd 读取模型上的 retry time cycle。仍有次数时,作业移入 timer job 并计算下一次到期时间;次数耗尽或异常不可恢复时,作业进入 dead letter。异常消息和堆栈也随新作业实体保存。
分支、失败与扩展
| 分流条件 | 源码行为 | 排障入口 |
|---|---|---|
| 流程定义不存在或已挂起 | 启动命令在创建 execution 前抛错 | StartProcessInstanceCmd、ProcessInstanceHelper |
| BPMN 校验有 error | BpmnParse 汇总错误并抛出 FlowableException;warning 只记录日志 | 部署异常、资源名、XSD 和 ProcessValidator 输出 |
| 节点同步执行 | 行为与后续 operation 留在当前命令事务 | API 异常、事务回滚、执行监听器与 Delegate |
节点 async="true" | 当前事务创建作业,后台事务执行行为 | ACT_RU_JOB、锁字段、Async Executor 日志 |
| User Task skip expression 为真 | 任务插入后立即删除,不触发普通任务事件并直接续行 | 模型表达式、变量值、SkipExpressionUtil |
| complete listener 抛 BPMN Error | 错误按模型传播,普通 TriggerExecutionOperation 不再计划 | Task listener、边界错误事件、execution 当前节点 |
| 作业排队时已被删除 | 执行命令重新查询后直接跳过 | ExecuteAsyncRunnableJobCmd 的调试日志 |
| 作业执行失败 | 新事务减少重试并转 timer job;耗尽后转 dead letter | timer job、dead letter、异常堆栈 |
| 集群竞争同一作业 | lock owner、lock expiration 与乐观锁决定归属 | 获取线程日志、数据库时钟、锁过期配置 |
| Flowable 5 流程定义 | 部署、启动、任务和作业命令进入兼容 Handler | flowable5-* classpath 与兼容开关 |
| 扩展需求 | 合适入口 | 代价与边界 |
|---|---|---|
| 给所有 API 增加审计、租户或重试 | 自定义 CommandInterceptor | 位于事务主路径,错误会影响所有命令 |
| 在引擎初始化前后替换组件 | EngineConfigurator.beforeInit() / configure() | 依赖初始化顺序和 priority,升级时需复核内部对象 |
| 实现业务 Service Task | JavaDelegate、delegate expression | 能访问流程变量和 Spring Bean,也可能执行任意副作用 |
| 实现新的 BPMN 节点行为 | ActivityBehaviorFactory / ActivityBehavior | 属于内部 SPI,需要理解 Agenda、execution 和历史记录 |
| 监听任务、流程和作业事件 | BPMN listener、FlowableEventListener | 同步 listener 可能延长事务;异常语义取决于监听器类型 |
| 运行中跳转、撤回或加签 | ChangeActivityStateBuilder | 会重写执行树和关联任务,应覆盖并发、多实例、子流程与历史测试 |
安全上最容易被低估的是模型内容。ProcessExpressionManager 接收可调用的 Bean 映射;DefaultActivityBehaviorFactory 为 Class Delegate、Delegate Expression、Script、Shell、HTTP 和 Mail 等模型行为创建执行对象。流程定义应像可执行代码一样经过版本控制、评审和发布授权,REST 暴露也应另行配置认证、租户隔离和网络策略。
数据表如何对应运行状态
| 前缀 | 典型表 | 含义 |
|---|---|---|
ACT_RE_* | ACT_RE_DEPLOYMENT、ACT_RE_PROCDEF、ACT_RE_MODEL | Repository,部署资源和流程定义 |
ACT_RU_* | ACT_RU_EXECUTION、ACT_RU_TASK、ACT_RU_VARIABLE、ACT_RU_JOB | Runtime,当前执行树、任务、变量和作业 |
ACT_HI_* | 历史流程、活动、任务和变量表 | History,已经发生的审计与查询数据 |
当流程停在 User Task 时,重点关联 ACT_RU_EXECUTION、ACT_RU_TASK 和 identity link;异步节点则增加 ACT_RU_JOB。执行结束后,EndExecutionOperation 会清理运行时执行数据;是否还能查询完整轨迹取决于历史级别和异步历史配置。直接改表会绕过实体缓存、版本字段、事件和历史管理,不适合作为常规修复手段。
设计取舍
命令统一事务边界。 对外 Service 不自行拼接事务和持久化细节,拦截器链让日志、重试、上下文与扩展按统一顺序工作。代价是自定义拦截器位于所有 API 的主路径,错误影响面很大。
模型与行为分离。 部署期完成解析和行为绑定,运行期围绕 execution 与 behavior 工作,既保留 BPMN 语义,也允许替换行为工厂。内部 SPI 与初始化顺序较复杂,跨大版本升级需要重新核对。
Agenda 显式表达控制流。 operation 队列为网关、边界事件、子流程和调试监听提供统一执行面。理解问题时需要同时掌握 execution tree、模型节点和 Agenda,学习成本高于普通状态机。
等待状态和作业都持久化。 长流程不占用线程,异步交接、集群竞争、重试和死信也有数据库状态可查。数据库因此同时承担业务状态和协调压力,索引、清理、锁等待与时钟一致性都进入运维范围。
多引擎共享基础服务。 BPMN 与 CMMN 复用任务、变量、作业和身份关系服务,但保留各自语义。共享降低重复,也让配置器、schema 和生命周期之间的关系更难从单一模块看全。
适用场景与同类选择
Flowable 适合需要 BPMN 2.0 可视化流程、人工任务、长期等待、审计历史、定时器和 Java/Spring 深度嵌入的系统,例如审批、订单履约、客户服务和跨系统编排。若流程主要由开发者以代码定义、没有 BPMN 建模与人工任务需求,代码优先的持久执行框架可能更直接;若只是简单状态枚举,完整流程引擎会引入过多概念和表。
| 方案 | 建模与运行形态 | 更适合的侧重点 | 采用时应核对 |
|---|---|---|---|
| Flowable | Java 可嵌入或 REST;BPMN、CMMN、DMN 与共享服务 | Spring 应用中的流程、案例、决策和人工任务统一引擎 | Flowable 8 基础栈、数据库容量、模型发布治理 |
| Camunda 7 | Java 流程引擎与 BPMN/DMN 生态 | 已有 Camunda 7 模型、插件和运维体系 | 产品生命周期、版本支持与后续迁移路线 |
| Temporal | 代码定义的持久工作流,不以 BPMN 图为核心 | 开发者主导的分布式长事务、重试和补偿 | 与业务建模人员的协作方式、SDK 与服务端运维 |
真正有区分度的问题是:谁维护流程,状态要保留多久,是否需要人工任务,业务代码能否接受引擎侵入,数据库或独立服务由谁运维,以及升级是否允许改变 Spring/Jackson 基线。
构建与调试问题
下面的排障入口来自固定提交的静态源码阅读和 Flowable 官方配置文档;这些内容给出检查顺序与可观察状态,不代表本次任务已经在目标仓库运行验证。
启动时报数据库表不存在
先确认 schema update 配置与环境目标,参见 Flowable 数据库配置文档。开发环境可让引擎创建表;生产环境应使用对应数据库和版本的 SQL 脚本。验证时不要只看一张 ACT_RU_* 表,还要检查被启用的 common、identity、task、variable、job 和 history schema。
Spring Boot 启动了但流程没有自动部署
检查 FlowableProperties 中的 process-definition-location-prefix、文件后缀、check-process-definitions 和 deployment mode。自动配置先发现 Resource,再由 SpringProcessEngineConfiguration.start() 调用部署策略。日志中的 “No deployment resources were found for autodeployment” 表明没有发现资源,不等同于引擎创建失败。
完成任务后流程没有继续
先确认任务属于 BPMN 还是 CMMN;再检查 TaskHelper.completeTask() 是否完成任务、complete listener 是否传播了 BPMN Error、execution 当前节点是否仍为 User Task。普通路径会计划 TriggerExecutionOperation。
异步节点长期不执行
按 AcquireAsyncJobsDueRunnable 的状态顺序排查:ACT_RU_JOB 是否有记录,due date 是否到期,lock owner 是否长期不释放,执行器是否 active,线程池是否满,作业是否已转到 timer、suspended 或 dead letter 表。集群中偶发乐观锁日志可能只是正常竞争,持续锁超时则要检查节点存活、数据库时钟和锁时长。
Service Task 重复调用外部系统
Flowable 的作业重试保证流程继续尝试,不自动保证外部副作用只发生一次。Delegate 调用支付、库存、通知等系统时应传递业务幂等键,并把“外部成功但本地事务回滚”的情况纳入对账或补偿设计。
升级到 Flowable 8 后依赖冲突
先核对 Flowable 8.0.0 Release 所说明的 Spring Boot 4、Spring Framework 7、Jackson 3 和 JUnit 5 基础栈。若现有应用仍固定在 Spring Boot 3,不应只替换 Flowable 版本号。Jackson 2 兼容层也不代表所有第三方扩展都已适配 Jackson 3。
后续阅读路径
准备扩展业务节点时,从 JavaDelegate、Service Task 解析处理器和 ActivityBehaviorFactory 开始;排查同步流程,从 Service API 进入 Command,再看 Agenda 和 execution;排查异步失败,则按 job、timer job、dead letter 的状态迁移阅读 AcquireAsyncJobsDueRunnable、ExecuteAsyncRunnable 和 JobRetryCmd。
若要理解 Flowable 全套平台,下一步再分别进入 CMMN 的 plan item 生命周期、DMN 的 decision execution、Event Registry 的入站与出站通道,以及这些引擎如何通过 EngineConfigurator 挂到 App Engine。阅读顺序仍应以一次真实业务链为主,不必按 90 个模块逐个展开。
阅读 Flowable 时可以沿四层定位问题:部署阶段把标准模型编译成带行为的内存定义;运行阶段用 Command 组织事务边界,用 Agenda 推进执行树;数据库保存等待位置,作业服务负责恢复异步工作。这样可以把复杂模块映射回一次具体业务链。