news 2026/5/8 17:02:51

为什么我们没用Activiti?数环通iPaaS自研流程引擎的决策复盘

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
为什么我们没用Activiti?数环通iPaaS自研流程引擎的决策复盘

写在前面

每次跟技术同行聊到"我们的流程引擎是自研的"这个话题,对方的第一反应几乎都一样:“为什么不用开源的?Activiti/Camunda不香吗?”

说实话,我们内部当年做这个决策的时候,也纠结了很久。自研意味着更大的投入、更长的周期、更多的坑要自己填。但五年多走下来,回过头看,这是数环通iPaaS平台做得最正确的技术决策之一。

这篇文章想把这个决策过程拆开来讲:当初看了哪些方案,为什么不合适,自研的引擎做成了什么样,踩了哪些坑,最终的收益到底体现在哪里。


市面上的开源流程引擎,各有各的"但是"

Activiti / Camunda — BPMN阵营

这两个可能是Java生态里知名度最高的流程引擎了。基于BPMN 2.0规范,有成熟的流程设计器,社区生态丰富。

优点

  • BPMN标准规范,流程定义可移植
  • 有完整的人工审批、会签、加签等能力
  • 丰富的事件(信号事件、消息事件、定时事件)
  • 商业版Camunda有不错的监控后台

但是

这两个引擎的设计初衷是企业内部审批流程——人工任务在流程中占主导,节点之间可能间隔数小时甚至数天。所以它们的执行模型是"事件驱动 + 数据库持久化每一步状态"。

对iPaaS场景来说,问题在于:

  1. 性能模型不匹配。iPaaS流程是"API调用链",一个流程可能调用5-20个外部API,全程几秒到几十秒搞定。每一步都做数据库持久化?在日均百万执行量下,这个IO开销是致命的。
  2. 流程定义过重。BPMN XML冗长复杂,一个简单的"触发器→数据转换→调API"就要写几十行XML。而iPaaS的流程本质上就是一条数据管道,不需要泳道、不需要人工节点。
  3. 多租户能力弱。Activiti的多租户只是在数据层面隔离,并发控制、资源限制、流量管控全部需要自己做。
  4. 扩展成本高。想加一个"连接器调用"的节点类型?得深入理解它的ServiceTask和JavaDelegate机制,跟自有的连接器体系做适配。改造成本不亚于自研。

Temporal / Cadence — 工作流编排阵营

Temporal(原Uber的Cadence项目衍生)是这几年的新秀,在微服务编排领域口碑很好。

优点

  • 天然分布式,支持集群部署
  • Activity执行自动重试和超时处理
  • 支持长时运行工作流(几天甚至几个月)
  • 工作流的持久化和恢复机制很优秀
  • 有信号和查询能力

但是

  1. 技术栈依赖重。需要部署独立的Temporal Server集群(依赖Cassandra或MySQL + ElasticSearch),运维成本不低。对于一个本身就是SaaS产品的平台来说,多一套基础设施就多一分出问题的概率。
  2. 编程模型限制。Temporal要求每个Activity是一个独立函数,工作流通过代码编排。而iPaaS的流程是用户在前端可视化拖拽出来的,是JSON配置驱动的。两种范式之间的转换层会非常厚。
  3. 国内生态弱。文档、社区支持主要面向英文用户,国内SaaS特有的签名算法、认证方式等适配全靠自己。
  4. 计费困难。SaaS产品需要按执行次数、步骤数计费。Temporal本身不提供这种粒度的度量,需要额外开发。

Apache Airflow — 数据管道阵营

Airflow在数据工程领域是标配,DAG调度能力很强。

优点

  • DAG定义直观,Python代码定义流程
  • 调度能力强(Cron、依赖调度)
  • 有成熟的任务重试和告警机制
  • 社区庞大,Operator丰富

但是

  1. Python生态。数环通的技术栈是Java,引入Airflow意味着混合语言部署,增加复杂度。
  2. 批处理导向。Airflow是为ETL批处理设计的——T+1跑、按天调度。iPaaS需要的是实时事件驱动,一个钉钉审批通过,2秒内触发后续流程。
  3. 不适合高并发短任务。Airflow一个Worker进程能处理的并发DAG数很有限,远达不到iPaaS单节点数千并发的需求。
  4. 没有可视化编排能力。面向开发者,不面向业务用户。

Node-RED / n8n — 轻量集成阵营

这类工具跟iPaaS最像,都是可视化流程编排 + 连接器生态。

优点

  • 低代码可视化编排
  • 连接器生态丰富(n8n 400+节点)
  • 部署简单
  • 前端交互设计优秀

但是

  1. 单机模型。Node-RED和n8n都是单进程设计,没有集群调度能力。当执行量上去后,只能堆机器,没有统一的队列和调度。
  2. 没有租户隔离。作为自部署工具很合适,作为多租户SaaS平台的底座完全不行。
  3. 执行模型太简单。没有暂停恢复、没有并发控制、没有优雅停机。一个流程卡住可能影响整个进程。
  4. Node.js性能天花板。在大数据量同步场景下(比如一次拉取10万条订单做ETL),受限于V8内存和单线程模型。

我们需要的引擎,长什么样?

把上面的分析综合下来,数环通iPaaS的流程引擎需要满足这些核心诉求:

需求维度具体要求
执行模型内存态运行,只在必要时持久化(而非每步持久化)
并发能力单节点支撑数千流程并发执行
多租户租户级并发隔离、资源限制、公平调度
驱动方式JSON配置驱动,支持前端可视化编排
扩展能力Handler插件化,新增节点类型不改引擎核心
运维能力暂停恢复、优雅停机、超时管控
计费支持步骤级计数、连接器维度统计
重试机制节点级自动重试,支持固定间隔和动态间隔
并行分支Fork/Join并行执行

没有一个开源方案能同时满足上面这些要求。与其在别人的框架上缝缝补补,不如从零设计一个专为iPaaS场景优化的轻量执行引擎


自研引擎的核心架构

执行模型:Handler Chain + Step推进

引擎的核心执行模型非常简洁。一个流程在运行时由三个核心对象组成:

  • FlowInstance:流程定义,包含一组Handler和它们的连接关系
  • Execution:一次流程执行的上下文,承载所有运行时数据
  • ExecutionHandler:每个节点的执行逻辑,通过nextHandler串联成链

执行过程就是沿着Handler Chain逐个推进:

TriggerHandler → ConnectorHandler → DataTransformHandler → ConnectorHandler → EndEvent

每一步(Step)执行完成后,Runner检查是否有中断信号,没有就推进到下一个Handler:

// 核心执行循环handler.execute(ec,callback);// 步骤完成后检查中断if(interruptControl.executeInterrupt(ec)){return;// 响应中断信号}// 推进下一步ec.newStep(currentStep.getNextHandler(),...);

为什么这么设计而不是状态机?

状态机的问题是每次状态变迁都要做持久化(否则无法恢复),这在高频执行场景下是瓶颈。我们的做法是:执行过程全部在内存中完成,只在中断时才做快照持久化。99%的流程一气呵成跑完,不需要任何IO开销。那1%被中断的流程,通过Snapshot恢复到其他节点继续执行。

并发控制:Semaphore + 多级水位

单机数千并发流程执行,最怕的是雪崩。我们设计了一套多级水位的并发控制机制:

// 6级执行决策1.子流程/恢复执行 → 直接放行(skipQueue)2.流程维度并发数超限 → 快速失败或排队3.低于安全水位 → 直接执行4.队列中有排队 → 直接排队(保证公平性)5.达到警告水位 → 快速失败或排队6.组织维度并发超限 → 快速失败或排队

底层用Java Semaphore控制许可数:

publicclassConcurrencyControl{privatefinalSemaphoresemaphore;publicvoidacquire(Executionec){semaphore.acquire();// 获取许可,满了就等}publicvoidrelease(Executionec){semaphore.release();// 执行完释放}publicdoublepermitsUsageRate(IntegerpermitsLimits){// (总量-余量)/总量 = 使用率return(permitsLimits-availablePermits())/permitsLimits;}}

更巧妙的是,许可数支持通过Nacos动态调整。线上出现性能问题时,可以热调整并发上限而不用重启:

publicvoidchangePermits(intoriginal,inttarget){if(original<target){increase(target-original);// 增加许可}else{decrease(original-target);// 逐个回收许可}}

排队机制:不能排的快速失败,能排的入队等待

并非所有流程都适合排队。同步API调用(用户在等响应的)不能排——排10秒用户早超时了。异步的事件触发流程可以排——反正多等几秒用户感知不到。

privatebooleanunableEnqueue(Executionec){// 试运行不排队if(ec.getFlow().isTest())returntrue;// 同步API触发不排队if(TriggerTypeEnum.API_MANAGE_TRIGGER.equals(triggerType)&&!isAsync(ec))returntrue;returnfalse;}

排队的流程会做一次快照持久化,等资源空闲后由调度器取出恢复执行。这让引擎在高峰期不会崩溃,只是响应变慢了一点——降级而不是崩溃。

Fork/Join并行:不只是多线程那么简单

iPaaS流程中经常需要并行执行多个分支——比如"同时向钉钉和飞书发通知"、“把一批数据拆成多个批次并行推送”。

我们实现了Fork/Join模型:

protectedList<Execution>runForkContext(Executionec,ExecutionForkContextforkContext){for(ExecutionForkRecordrecord:forkContext.getRecords()){ExecutionforkedEc=ec.fork();// 派生子执行上下文forkedEc.forkStep(record.getCurrentStep().getCurrentHandler(),...);if(isSync){runner.runSync(forkedEc);// 单分支直接同步执行}else{runner.run(forkedEc);// 多分支并行执行}}join(ec.getCurrentStep().getStepId());// 等待所有分支完成}

注意这里的优化:如果只有一个分支(实际不需要并行),直接同步执行,避免线程切换开销。这是我们在线上发现的——大量"并行网关"实际只有一条分支(另一条被条件过滤掉了),如果还做线程派发,纯浪费。

Fork等待用Semaphore实现:Fork时acquire,所有分支完成后在JoinGateway中release。简洁且线程安全。

步骤级重试:失败了不用从头来

外部API调用是不稳定的——网络抖动、限流、超时都可能发生。引擎内置了步骤级重试机制:

// 支持两种重试策略// 1. 固定间隔:每隔N秒重试一次// 2. 动态间隔:[1s, 3s, 10s] 逐次递增for(intcurExecCount=start;curExecCount<=stepRetryCount;curExecCount++){// 恢复本步骤的上下文ec.putParameter(currentHandler.getName(),parameter);ec.getCurrentStep().setOutput(output);ec.setNextHandler(nextHandler);ec.setError(error);// 重新执行doExecuteHandler(currentHandler,ec,callback);if(ec.getError()==null)break;// 成功了就跳出Thread.sleep(interval);// 等待后重试}

关键设计点:只对外部连接器调用做重试,内部逻辑节点不重试。因为内部节点的失败通常是逻辑错误(比如字段映射配错),重试也不会好。而外部调用的失败大多是瞬时的,重试有意义。

中断信号机制:无侵入的流程控制

引擎支持多种中断类型——手动暂停、部署暂停、手动停止、执行失败、排队中断:

publicenumInterruptTypeEnum{MANUAL_PAUSE,// 用户手动暂停MANUAL_STOP,// 用户手动停止DEPLOY_PAUSE,// 优雅停机暂停EXECUTE_FAILED,// 执行失败QUEUE// 排队等待}

中断信号存在本地 Cache中(性能考虑),执行器在每个步骤间隙检测一次。检测到信号后,流程在当前步骤完成后停下来,做快照持久化。

这套机制的精巧在于——对Handler实现完全透明。写一个新的连接器Handler,不需要关心中断逻辑,引擎在外层统一处理。这大大降低了开发新节点类型的心智负担。

超时监控:不让僵尸流程占着资源

每300毫秒巡检一次所有运行中的流程,超过配置时间(默认6小时)的直接终结:

privateJobResultmonitorTimeoutExecutions(){for(Map.Entry<String,Execution>e:executions.entrySet()){if(ec.getRuntime().getDuration()>executionTimeout){timeouts.add(ec);}}for(Executionec:timeouts){ec.setError(newIpaasException(ResultCode.EXECUTION_TIMEOUT));complete(ec);// 释放资源}}

看似简单,但如果用Activiti做这个事情,你需要额外部署一个定时任务去查数据库里的运行中流程,然后发信号去中断——链路长、延迟高。我们的实现是纯内存操作,毫秒级响应。

多租户感知:每一层都有隔离

从并发控制到队列排队,引擎的每一层都是租户感知的:

  • 并发许可:全局信号量控制总并发,避免单租户打爆整台机器
  • 组织级并发限制groupConcurrencyLimit限制单个组织的最大并发数
  • 排队公平性:队列按组织隔离,一个组织排队不影响其他组织
  • 计费统计:按组织+流程维度统计步骤数、连接器调用数

这些能力在开源引擎上几乎不可能开箱获得,都是自研才能做到这种粒度的嵌入。


自研的真实难度

说了这么多好处,也得诚实地讲讲自研的代价。

第一年最痛苦。从执行模型设计、Handler抽象、到线程模型调优,前半年基本在反复推翻重来。特别是并发模型——一开始用的是线程池直接submit,结果高并发下线程数爆炸。后来改成Semaphore许可模型才稳定下来。

边界场景多。Fork/Join看似简单,但嵌套Fork(Fork里面再Fork)、Fork某个分支失败另一个还在跑、Fork和中断信号的交互……这些边界场景的组合爆炸,每一个都是生产事故的潜在来源。

没有参考。用开源方案至少StackOverflow上能搜到别人遇到的问题。自研引擎的坑,全世界只有你自己踩过,只能靠线上案例一个一个打磨。

测试难度大。流程引擎的测试不是简单的单元测试能覆盖的。并发竞态、超时边界、中断时机……这些需要大量的集成测试和混沌工程手段。

我们团队前后投入了3人×12个月才把核心引擎打磨到生产级稳定。这不是一个"随便试试"的投入量。


但回报也是实实在在的

性能方面:单节点稳定支撑2000+并发流程执行。如果用Activiti + MySQL这套方案,同等并发量可能需要3-5倍的机器资源。

灵活性方面:新增一种节点类型(比如CDC触发器、OPC UA协议节点),只需要实现ExecutionHandler接口,不需要动引擎核心代码。过去三年我们加了十几种新节点类型,引擎主循环的代码几乎没改过。

运维方面:优雅停机、暂停恢复、动态调参、超时管控——这些能力是生产环境的刚需,自研可以做到丝滑集成。如果是基于开源方案改造,每一个都是大动作。

产品差异化:排队机制、多级水位降级、租户并发隔离——这些精细化的资源管控能力,直接转化为了SaaS产品的卖点(“不限量执行” vs “保证SLA”)。

发版速度:遇到线上问题,我们能在小时内定位到引擎层面的原因并修复。如果是开源方案,得先判断是框架Bug还是自己的使用问题,光排查就可能花几天。


总结:什么时候该自研,什么时候不该

说到底,是否自研流程引擎,取决于你的业务场景和引擎的贴合度

建议用开源方案的场景

  • 企业内部审批流程(Activiti/Camunda很合适)
  • 数据ETL调度(Airflow是最佳选择)
  • 微服务编排且团队熟悉Temporal(上Temporal)
  • 个人或小团队做集成工具(n8n开箱即用)

建议自研的场景

  • 做SaaS平台,需要多租户资源隔离
  • 高并发短任务(秒级执行、万级QPS)
  • 需要深度定制执行模型(配置驱动、非代码驱动)
  • 流程执行是核心竞争力,不是辅助功能
  • 团队有足够的工程投入能力

对数环通来说,流程引擎是整个产品的心脏。它的性能、稳定性、灵活性直接决定了客户体验。这种核心中的核心,交给自己掌控是值得的投入。

五年打磨下来,这个引擎每天稳定执行着千百万条自动化流程,支撑着上万家企业的集成需求。它不完美——代码里还有不少待优化的地方——但它是真正为iPaaS场景量身打造的。

这,就是自研的意义。


数环通iPaaS——自研流程引擎驱动的企业级集成平台,1000+应用连接器,亿万级流程执行。了解更多:https://www.solinkup.com

标签:#流程引擎 #iPaaS #Activiti #Camunda #Temporal #自研引擎 #并发控制 #微服务编排 #数环通 #工作流引擎 #企业集成 #低代码平台

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

FModel终极指南:5步快速掌握虚幻引擎资源提取工具

FModel终极指南&#xff1a;5步快速掌握虚幻引擎资源提取工具 【免费下载链接】FModel Unreal Engine Archives Explorer 项目地址: https://gitcode.com/gh_mirrors/fm/FModel FModel是一款功能强大的虚幻引擎Pak文件资源提取工具&#xff0c;专为游戏开发者、资源爱好…

作者头像 李华
网站建设 2026/5/8 17:01:51

锁仓投票:一场用“时间换权力”的DeFi实验

上一期我们拆解了以太坊的EIP-1559——那台24小时不间断运行的“自动回购机器”&#xff0c;如何通过燃烧手续费让网络使用直接转化为Token持有者的价值。它的精妙之处在于&#xff1a;持有者无需做任何操作&#xff0c;就能从全网的活跃度中获益。但加密世界里还有另一批设计者…

作者头像 李华
网站建设 2026/5/8 17:01:04

为什么有些论文改了很多遍,结果还不如系统优化一次?

每年论文季&#xff0c;很多学生都会进入一种特别熟悉的状态&#xff1a;白天改&#xff0c;晚上改&#xff1b; 导师意见改完&#xff0c;再按评审意见改&#xff1b; 这一版刚发出去&#xff0c;下一版又回来了。有的人一篇论文能改十几遍。但最让人崩溃的&#xff0c;往往不…

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

对比直接使用官方API通过Taotoken调用在费用上的差异感受

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 对比直接使用官方API与通过Taotoken调用在费用上的差异感受 1. 项目背景与迁移动机 我负责维护一个内部使用的小型项目&#xff0…

作者头像 李华