news 2026/8/17 19:04:52

Flowable 事务与并发控制:保证流程一致性的关键机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Flowable 事务与并发控制:保证流程一致性的关键机制

Flowable 事务与并发控制:保证流程一致性的关键机制

【免费下载链接】flowable-userguideFlowable最新中文文档,ai自动生成BPM体验地址:http://flow.je4.cn/#/login项目地址: https://gitcode.com/gh_mirrors/fl/flowable-userguide

一、Flowable 事务模型:默认的"等待状态"机制

Flowable 是当前最流行的开源工作流引擎之一,它天然以事务方式执行 BPMN 流程。当你启动一个流程实例、完成任务或发送信号时,Flowable 会沿着流程图的每一条活跃路径做深度优先遍历,一直推进到**等待状态(Wait State)**才停下来。

所谓等待状态,就是"稍后"才执行的任务节点,比如用户任务、接收消息任务、定时器事件等。到达等待状态后,Flowable 会把当前执行状态持久化到数据库,等待下一次触发。这一点非常重要:一次 API 调用对应一个事务单元,这个单元要么全部成功,要么全部回滚。

上图展示了事务边界的两种切法:左侧是默认的同步方式,用户任务"补充收货地址"和服务任务"校验地址"处于同一事务中,一旦校验失败,任务完成操作也会回滚,用户任务依然留在数据库里;右侧则通过异步延续,把"生成发票"拆成了独立的事务。

二、异步延续:自定义事务边界的核心武器

很多业务场景并不希望"一损俱损"。例如用户任务完成后要生成发票并发给客户,如果生成发票失败,用户任务的完成结果不应该被回滚。此时就需要**异步延续(Async Continuation)**来拆事务。

只需在 BPMN 节点上添加flowable:async="true"扩展属性即可:

<serviceTask id="service1" name="Generate Invoice" flowable:class="my.custom.Delegate" flowable:async="true" />

配置了异步延续后,Flowable 会在后台创建一个Job(作业)并持久化到数据库,由异步执行器(Async Executor)的线程池取出来执行。flowable:async可以用于taskserviceTaskscriptTasksendTaskuserTasksubProcesscallActivity等几乎所有任务类型。

📌适用场景:耗时操作、与外部系统交互、非核心业务逻辑,都适合拆成异步事务,避免长时间占用数据库连接和调用线程。

三、失败重试机制:让流程更健壮

Flowable 默认配置下,一个 Job 执行异常时会自动重试 3 次。但对于真实业务,3 次往往不够灵活,你可以通过flowable:failedJobRetryTimeCycle自定义重试次数和重试间隔(遵循 ISO 8601 时间格式):

<serviceTask id="failingServiceTask" flowable:async="true" flowable:class="org.flowable.engine.test.jobexecutor.RetryFailingDelegate"> <extensionElements> <flowable:failedJobRetryTimeCycle>R5/PT7M</flowable:failedJobRetryTimeCycle> </extensionElements> </serviceTask>

上面的配置表示:重试 5 次,每次间隔 7 分钟。

⚠️重要提醒:如果服务带有非事务性副作用(比如调用外部支付接口),重试可能造成副作用重复执行。这类场景必须做好幂等设计。

四、独占任务(Exclusive Jobs):并发安全的默认防线

为什么要独占?

看下面这个经典场景:并行网关后跟着三个异步服务任务,它们各自生成一个 Job,可能被线程池同时取走执行。当三个分支同时到达并行合并网关时,每个事务都"看不到"其他分支的到来,于是都认为需要等待其他分支——结果谁也不会继续,流程实例永远卡死在合并网关。

乐观锁兜底

Flowable 用乐观锁解决这个问题:多个事务基于同一行数据做决策时,都会递增该数据库行的版本号。先提交者获胜,其他事务抛出乐观锁异常,随后由 Job 执行器自动重试,最终顺利通过合并网关。

独占任务如何工作

从 Flowable 6 开始,所有异步延续和定时器事件默认都是独占的(Exclusive)。独占意味着:同一流程实例的独占 Job 绝不会并发执行——执行器会一次性获取同一流程实例的所有独占 Job,交给同一个工作线程串行执行。

如果确实想并发,可以显式关闭:

<serviceTask id="service" flowable:expression="${myService.performBooking(hotel, dates)}" flowable:async="true" flowable:exclusive="false" />

🎯性能误区澄清:独占只限制"同一流程实例"内的 Job 串行,不同流程实例的 Job 依然并行执行。反而因为节点亲和性,后续 Job 所需的流程数据已在本地缓存,通常整体吞吐更优。

五、异步执行器:V6 时代的作业调度核心

从 Flowable 6 开始,异步执行器是唯一可用的 Job 执行机制,它采用线程池 + 数据库表分区的架构:

  • 定时器作业(如边界定时事件)存放在ACT_RU_TIMER_JOB,到期后转为异步作业;
  • 异步作业flowable:async="true")写入ACT_RU_JOB表,提交后由事务监听器触发执行;
  • 重试超过上限的"死作业"进入ACT_RU_DEADLETTER_JOB死信表,供管理员人工排查;
  • 挂起流程相关的作业进入ACT_RU_SUSPENDED_JOB,让获取查询保持精简高效。

常用配置项(通过SpringProcessEngineConfiguration设置):

配置项默认值说明
asyncExecutorCorePoolSize2线程池核心线程数
asyncExecutorMaxPoolSize10线程池最大线程数
asyncExecutorNumberOfRetries3重试次数上限,超限进入死信表
asyncExecutorAsyncJobLockTimeInMillis5 分钟作业锁定时间,防止多节点重复执行
asyncExecutorResetExpiredJobsInterval60 秒清理过期锁定的周期

六、BPMN 事务子流程与补偿:长事务的正确姿势

事务子流程的三种结局

BPMN 的**事务子流程(Transaction Sub-process)**把一组活动圈成一个业务事务,有三种结局:

  1. 成功:正常流出,后续可用补偿事件撤销;
  2. 取消:到达取消结束事件,触发补偿后从取消边界事件流出;
  3. 风险终止:错误事件未被捕获,直接终止且不执行补偿

与 ACID 事务的本质区别

务必分清:BPMN 事务子流程 ≠ 数据库 ACID 事务。ACID 事务短小精悍、支持回滚;而 BPMN 事务可能持续数小时甚至数月(比如等待用户审批、等待订单履约),天然跨越多个 ACID 事务,因此失去隔离性和传统回滚能力

解决之道是补偿(Compensation):为已成功执行的活动配置补偿处理器,当事务取消时,逆序调用这些补偿处理器撤销业务影响,例如"取消酒店预订""取消航班预订"。

💡 更深入的事务子流程、补偿边界事件与补偿处理器实现,可参考官方中文文档 ch07b-BPMN-Constructs.adoc,异步执行器细节见 ch18-Advanced.adoc。

七、实践建议:新手避坑清单 ✅

  • 优先保持默认:异步延续默认独占,绝大多数场景直接使用即可,不要轻易关闭;
  • 拆分要克制:只有真正需要独立事务边界的环节才用flowable:async="true",过度拆分反而增加复杂度;
  • 做好幂等:凡是配置了重试的异步任务,业务逻辑必须能容忍重复执行;
  • 监控死信表:定期检查ACT_RU_DEADLETTER_JOB,及时发现反复失败的任务;
  • 补偿尽早设计:涉及跨系统、长周期的业务流程,从建模阶段就要规划补偿处理器;
  • 关注锁超时:长时间运行的流程实例,留意定时器锁与作业锁的过期重置配置。

总结

Flowable 通过事务边界的默认划分保证单元一致性,通过异步延续灵活拆分长流程,通过独占任务 + 乐观锁化解并行网关的并发冲突,再借助异步执行器完成可靠调度,最后用事务子流程 + 补偿支撑跨系统长事务。理解这五层机制,你就能设计出既高性能又高一致性的工作流应用。

如果你想动手实践,可以参考仓库中的 SQL 建表脚本(sql/create)和完整用户指南(V6.4.2/docs/userguide/src/zh_CN),边看边跑,很快就能掌握 Flowable 事务与并发控制的精髓!

【免费下载链接】flowable-userguideFlowable最新中文文档,ai自动生成BPM体验地址:http://flow.je4.cn/#/login项目地址: https://gitcode.com/gh_mirrors/fl/flowable-userguide

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/17 18:59:20

什么是混合搜索?

两种或更多检索方法。一个排序列表。 混合搜索是一种信息检索技术&#xff0c;它将两种或更多搜索方法&#xff08;例如词汇搜索/lexical 和语义搜索&#xff09;结合到一个统一的排序列表中&#xff0c;以提升相关性和召回率。最常见的组合是将全文词汇搜索与语义向量搜索结合…

作者头像 李华
网站建设 2026/8/17 18:58:30

小红的马【牛客tracker 每日一题】

小红的马 时间限制&#xff1a;3秒 空间限制&#xff1a;1024M 网页链接 牛客tracker 牛客tracker & 每日一题&#xff0c;完成每日打卡&#xff0c;即可获得牛币。获得相应数量的牛币&#xff0c;能在【牛币兑换中心】&#xff0c;换取相应奖品&#xff01;助力每日有…

作者头像 李华
网站建设 2026/8/17 18:57:43

Vue开发中如何彻底解决“Cannot read property of undefined”渲染错误

1. 项目概述&#xff1a;从“Cannot read property ‘xxx‘ of undefined”说起 如果你在用Vue开发项目&#xff0c;尤其是在处理动态数据渲染的时候&#xff0c;大概率见过这个老朋友&#xff1a; Error in render: “TypeError: Cannot read property ‘xxx‘ of undefined”…

作者头像 李华
网站建设 2026/8/17 18:55:11

NICO性能优化指南:基准测试与渲染调优的实用技巧

NICO性能优化指南&#xff1a;基准测试与渲染调优的实用技巧 【免费下载链接】nico a Game Framework in Nim inspired by Pico-8. 项目地址: https://gitcode.com/gh_mirrors/ni/nico NICO 是一个用 Nim 编写的轻量级游戏框架&#xff0c;其 API 深受 Pico-8 启发&…

作者头像 李华