业务场景
- 先聊下之前遇到一个场景,我们的业务会依赖好几个系统,先调用A系统获取部分数据,做部分计算,再调用B系统获取一部分数据,再做部分计算,再调用C系统拿到一部分,再最终计算得到最终结果。
- 对于不同的产品,每个流程都会不同,调用的系统也会不同。
业务分析
分析整个业务流程,会发现有几个需要解决的问题:
- 前端是异步调用,调用完成后,通过不断轮询的方式获取结果,万一被调用的某个系统响应超时了,整条业务就挂了,用户拿不到最终数据。
- 现在基本所有应用都是分布式,调用完各系统之后基本都涉及计算,如果并发控制的不好,用户对于同一个数据做多次请求,可能会导致最终计算结果不正确。
问题的解法
这个非常典型的流程型业务,这个系统需要承载较多的类似的业务流程,需要被解决的问题也比较类似。
- 架构设计上需要解决一个系统承载这么多的业务流程的可维护问题,不同的流程之前代码如何隔离,修改一个业务的代码如何更快速的分辨出是否会对其他业务造成影响。
- 解决技术上的通用性问题:在调用第三方系统的时候,如果发生了失败,这个过程需要有自动恢复的能力,减少因为第三方系统不稳定问题带来的业务失败。在并发情况下,需要保证整个业务流程的并发安全。
问题的抽象
基于上面的分析,把这个问题抽象出了“流程”,“任务”两个领域,一个业务对应一个流程,一个流程里面包含多个任务,每个环节由不同的任务完成,在运行时,任务实例各自保证重试,并发安全等能力。
在这样的抽象下,我们可以对我们的所有业务进行可视化管理和操作。可以看到每个业务流程都包含哪些任务代码,这样就可以提升我们的维护效率。
通过将失败重试和并发保证的能力封装在“任务”这个领域,就避免了在开发业务时需要关注这方面的技术问题。
领域模型设计
对于领域的设计,我们可以分成两大块:定义和运行。针对每个新的业务,首先我们定义好流程,包含流程的名称,业务描述,这个流程里面包含多少个任务。
在运行的时候,使用流程实例工厂,从流程定义中生产出一个流程实例运行。
如何实现
首先要解决的是让节点一个一个按照链的方式执行下来。所以在构建实例的时候,每个ProcessInstance里面会有一个HeaderTaskInstance作为头节点,每个节点里面会存下一个要执行的节点的引用。
线程安全问题: 每个Task的状态包含运行,完成,错误三个状态,在一个Task执行之前,首先拿到改Task的锁,然后执行Task,执行完Task后,释放锁。 如果执行的Task实例存储在数据库中,可以基于数据库的Task行实例实现分布式锁。
失败重试: 失败重试这种场景主要用于线程异步执行的场景,一个Task执行失败,状态会进入ERROR状态,需要依赖一个监听线程,拉取失败的Task并重新执行。
其他问题
通过上述的整理,可以实现一个简单的流程引擎 在一个业务流程中会遇到其他几个场景:
- 流程在执行的时候,会根据条件的不同,执行不同的下一个环节。这个时候,就需要提供具有路由能力的节点类型。也有可能会遇到某个任务是需要另外一个线程触发的,即需要一个人工类型的任务节点。所以对于任务,会有不同的任务类型。
- 一个流程实例在运行的时候,往往后面的任务会依赖前面的计算结果,这个可以通过在线程变量提中存放上下文的方式解决。
- 任务在重新执行的时候,所有的被执行的业务逻辑都需具备冪等性,这样才能保证整体业务逻辑的一致性。
优点
业界有比较成熟的BPMN流程引擎规范,也有很多已开源实现,为什么要自己实现一个流程引擎?
流程引擎对业务有比较强的侵入,不同的业务场景,对流程引擎的个性化能力要求也有不同,自己实现可能更好的服务自己的业务场景,遇到问题可以完全Hold住。