关于架构:工作流引擎架构设计

33次阅读

共计 12085 个字符,预计需要花费 31 分钟才能阅读完成。

原文链接: 工作流引擎架构设计

最近开发的平安治理平台新增了很多工单申请流程需要,比方加白申请,开明申请等等。最开始的两个需要,为了不便,也没多想,就间接开发了对应的业务代码。

但随着同类需要一直增多,感觉再这样写可要累死人,于是开始了工作流引擎的开发之路。查找了一些材料之后,开发了现阶段的工作流引擎,文章前面会有介绍。

尽管当初基本上能满足日常的需要,但感觉还不够智能,还有很多的优化空间,所以正好借此机会,具体理解了一些欠缺的工作流引擎框架,以及在架构设计上须要留神的点,造成了这篇文章,分享给大家。

什么是工作流

先看一下维基百科对于工作流的定义:

工作流(Workflow),是对工作流程及其各操作步骤之间业务规定的形象、概括形容。工作流建模,行将工作流程中的工作如何前后组织在一起的逻辑和规定,在计算机中以失当的模型表白并对其施行计算。

工作流要解决的次要问题是:为实现某个业务指标,利用计算机在多个参与者之间按某种预约规定主动传递文档、信息或者工作。

简略来说,工作流就是对业务的流程化形象。WFMC(工作流程治理联盟)给出了工作流参考模型如下:

举一个例子,比方公司办公的 OA 零碎,就存在大量的申请审批流程。而在解决这些流程时,如果每一个流程都对应一套代码,显然是不事实的,这样会造成很大水平上的代码冗余,而且开发工作量也会骤增。

这个时候就须要一个业务无关的,高度形象和封装的引擎来对立解决。通过这个引擎,能够灵便配置工作流程,并且能够自动化的依据配置进行状态变更和流程流转,这就是工作流引擎。

简略的工作流

那么,一个工作流引擎须要反对哪些性能呢?

这个问题并没有一个标准答案,须要依据理论的业务场景和需要来剖析。在这里,我通过一个工单流程的演进,从简略到简单,循序渐进地介绍一下都须要蕴含哪些根底性能。

最简略流程

最简略的一个流程工单,申请人发动流程,每个节点审批人一一审批,最终流程完结。

会签

在这个过程中,节点分成了两大类:简略节点和简单节点。

简略节点解决逻辑不变,仍然是解决完之后主动到下一个节点。简单节点比如说会签节点,则不同,须要其下的所有子节点都解决实现,能力到下一个节点。

并行

同样属于简单节点,其任何一个子节点解决实现后,都能够进入到下一个节点。

条件判断

须要依据不同的表单内容进入不同的分支流程。

举一个例子,比方在进行休假申请时,销假一天须要直属领导审批,如果大于三天则须要部门领导审批。

动静审批人

审批节点的审批人须要动静获取,并且可配置。

审批人的获取形式能够分以下几种:

  1. 固定审批人
  2. 从申请表单中获取
  3. 依据组织架构,动静获取
  4. 从配置的角色组或者权限组中获取

撤销和驳回

节点状态变更能够有申请人撤回,审批人批准,审批人驳回。那么在驳回时,能够间接驳回到开始节点,流程完结,也能够到上一个节点。更简单一些,甚至能够到后面流程的任意一个节点。

自动化节点

有一些节点是不须要人工参加的,比如说联动其余零碎主动解决,或者审批节点有工夫限度,超时主动生效。

个性化告诉

节点审批之后,能够配置不同的告诉形式来告诉相干人。

以上是我列举的一些比拟常见的需要点,还有像加签,代理,脚本执行等性能,如果都实现的话,应该会是一个宏大的工作量。当然了,如果指标是做一个商业化产品的话,性能还是须要更丰盛一些的。

但把这些常见需要点都实现的话,应该根本能够满足大部分的需要了,至多对于咱们零碎的工单流程来说,目前是能够满足的。

工作流引擎比照

既然这是一个常见的需要,那么须要咱们本人来开发吗?市面上有开源我的项目能够应用吗?

答案是必定的,目前,市场上比拟有名的开源流程引擎有 Osworkflow、Jbpm、Activiti、Flowable、Camunda 等等。其中:Jbpm、Activiti、Flowable、Camunda 四个框架同宗同源,先人都是 Jbpm4,开发者只有用过其中一个框架,基本上就会用其它三个了。

Osworkflow

Osworkflow 是一个轻量化的流程引擎,基于状态机机制,数据库表很少。Osworkflow 提供的工作流形成元素有:步骤(step)、条件(conditions)、循环(loops)、分支(spilts)、合并(joins)等,但不反对会签、跳转、退回、加签等这些操作,须要本人扩大开发,有肯定难度。

如果流程比较简单,Osworkflow 是一个很不错的抉择。

JBPM

JBPM 由 JBoss 公司开发,目前最高版本是 JPBM7,不过从 JBPM5 开始曾经跟之前不是同一个产品了,JBPM5 的代码根底不是 JBPM4,而是从 Drools Flow 从新开始的。基于 Drools Flow 技术在国内市场上用的很少,所有不倡议抉择 JBPM5 当前版本。

JBPM4 诞生的比拟早,起初 JBPM4 创建者 Tom Baeyens 来到 JBoss,退出 Alfresco 后很快推出了新的基于 JBPM4 的开源工作流零碎 Activiti,另外 JBPM 以 hibernate 作为数据长久化 ORM 也已不是支流技术。

Activiti

Activiti 由 Alfresco 软件开发,目前最高版本 Activiti7。Activiti 的版本比较复杂,有 Activiti5、Activiti6、Activiti7 几个支流版本,选型时让人昏头昏脑,有必要先理解一下 Activiti 这几个版本的倒退历史。

Activiti5 和 Activiti6 的外围 leader 是 Tijs Rademakers,因为团队外部一致,在 2017 年 Tijs Rademakers 来到团队,创立了起初的 Flowable。Activiti6 以及 Activiti5 代码曾经交接给了 Salaboy 团队,Activiti6 以及 Activiti5 的代码官网曾经暂停保护了。

Salaboy 团队目前在开发 Activiti7 框架,Activiti7 内核应用的还是 Activiti6,并没有为引擎注入更多的新个性,只是在 Activiti 之外的下层封装了一些利用。

Flowable

Flowable 是一个应用 Java 编写的轻量级业务流程引擎,应用 Apache V2 license 协定开源。2016 年 10 月,Activiti 工作流引擎的次要开发者来到 Alfresco 公司并在 Activiti 分支根底上开启了 Flowable 开源我的项目。基于 Activiti v6 beta4 公布的第一个 Flowable release 版本为 6.0。

Flowable 我的项目中包含 BPMN(Business Process Model and Notation)引擎、CMMN(Case Management Model and Notation)引擎、DMN(Decision Model and Notation)引擎、表单引擎(Form Engine)等模块。

绝对开源版,其商业版的性能会更弱小。以 Flowable6.4.1 版本为分水岭,大力发展其商业版产品,开源版本保护不及时,局部性能曾经不再开源版公布,比方表单生成器(表单引擎)、历史数据同步至其余数据源、ES 等。

Camunda

Camunda 基于 Activiti5,所以其保留了 PVM,最新版本 Camunda7.15,放弃每年公布两个小版本的节奏,开发团队也是从 Activiti 中决裂进去的,倒退轨迹与 Flowable 类似,同时也提供了商业版,不过对于个别企业应用,开源版本也足够了。

以上就是每个我的项目的一个大略介绍,接下来次要比照一下 Jbpm、Activiti、Flowable 和 Camunda。只看文字的话可能对它们之间的关系还不是很分明,所以我画了一张图,能够更清晰地体现每个我的项目的倒退轨迹。

那么,如果想要抉择其中一个我的项目来应用的话,应该如何抉择呢?我列举了几项我比拟关注的点,做了一张比照表格,如下:

Activiti 7 Flowable 6 Camunda JBPM 7
流程协定 BPMN2.0、XPDL、PDL BPMN2.0、XPDL、XPDL BPMN2.0、XPDL、XPDL BPMN2.0
开源状况 开源 商业和开源版 商业和开源版 开源
开发根底 JBPM4 Activiti 5 & 6 Activiti 5 版本 5 之后 Drools Flow
数据库 Oracle、SQL Server、MySQL Oracle、SQL Server、MySQL、postgre Oracle、SQL Server、MySQL、postgre MySQL,postgre
架构 spring boot 2 spring boot 1.5 spring boot 2 Kie
运行模式 独立运行和内嵌 独立运行和内嵌 独立运行和内嵌
流程设计器 AngularJS AngularJS bpmn.js
活跃度 沉闷 绝对沉闷 绝对沉闷
表数量 引入 25 张表 引入 47 张表 引入 19 张表
jar 包数量 引入 10 个 jar 引入 37 个 jar 引入 15 个 jar

Flowable 利用举例

如果抉择应用开源我的项目来开发本人的引擎,或者嵌入到现有的我的项目中,应该如何应用呢?这里通过 Flowable 来举例说明。

应用 Flowable 能够有两种形式,别离是内嵌和独立部署形式,当初来别离阐明:

内嵌模式

创立 maven 工程

先建一个一般的 maven 工程,退出 Flowable 引擎的依赖以及 h2 内嵌数据库的依赖,也能够应用 MySQL 数据库来做长久化。

<!-- https://mvnrepository.com/artifact/org.flowable/flowable-engine -->
<dependency>
  <groupId>org.flowable</groupId>
  <artifactId>flowable-engine</artifactId>
  <version>6.7.2</version>
</dependency>
<dependency>
  <groupId>com.h2database</groupId>
  <artifactId>h2</artifactId>
  <version>1.4.192</version>
</dependency>

创立流程引擎实例

import org.flowable.engine.ProcessEngine;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration;

public class HolidayRequest {public static void main(String[] args) {ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration()
      .setJdbcUrl("jdbc:h2:mem:flowable;DB_CLOSE_DELAY=-1")
      .setJdbcUsername("sa")
      .setJdbcPassword("")
      .setJdbcDriver("org.h2.Driver")
      .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);

    ProcessEngine processEngine = cfg.buildProcessEngine();}

}

接下来,咱们就能够往这个引擎实例上部署一个流程 xml。比方,咱们想建设一个员工销假流程:

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:activiti="http://activiti.org/bpmn"
             typeLanguage="http://www.w3.org/2001/XMLSchema"
             expressionLanguage="http://www.w3.org/1999/XPath"
             targetNamespace="http://www.flowable.org/processdef">

    <process id="holidayRequest" name="Holiday Request" isExecutable="true">

        <startEvent id="startEvent"/>
        <sequenceFlow sourceRef="startEvent" targetRef="approveTask"/>

<!--        <userTask id="approveTask" name="Approve or reject request"/>-->
        <userTask id="approveTask" name="Approve or reject request" activiti:candidateGroups="managers"/>

        <sequenceFlow sourceRef="approveTask" targetRef="decision"/>

        <exclusiveGateway id="decision"/>
        <sequenceFlow sourceRef="decision" targetRef="externalSystemCall">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[${approved}
        ]]>
            </conditionExpression>
        </sequenceFlow>
        <sequenceFlow sourceRef="decision" targetRef="sendRejectionMail">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[${!approved}
        ]]>
            </conditionExpression>
        </sequenceFlow>

        <serviceTask id="externalSystemCall" name="Enter holidays in external system"
                     activiti:class="org.example.CallExternalSystemDelegate"/>
        <sequenceFlow sourceRef="externalSystemCall" targetRef="holidayApprovedTask"/>

<!--        <userTask id="holidayApprovedTask" name="Holiday approved"/>-->
        <userTask id="holidayApprovedTask" name="Holiday approved" activiti:assignee="${employee}"/>

        <sequenceFlow sourceRef="holidayApprovedTask" targetRef="approveEnd"/>

        <serviceTask id="sendRejectionMail" name="Send out rejection email"
                     activiti:class="org.flowable.SendRejectionMail"/>
        <sequenceFlow sourceRef="sendRejectionMail" targetRef="rejectEnd"/>

        <endEvent id="approveEnd"/>

        <endEvent id="rejectEnd"/>

    </process>

</definitions>

此 xml 是合乎 bpmn2.0 标准的一种规范格局,其对应的流程图如下:

接下来,咱们就把这个文件传给流程引擎,让它基于该文件,创立一个工作流。

RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deployment = repositoryService.createDeployment()
  .addClasspathResource("holiday-request.bpmn20.xml")
  .deploy();

创立后,理论就写到内存数据库 h2 了,咱们还能够把它查出来:

ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
  .deploymentId(deployment.getId())
  .singleResult();
System.out.println("Found process definition :" + processDefinition.getName());

创立工作流实例

创立工作流实例,须要提供一些输出参数,比方咱们创立的员工销假流程,参数就须要:员工姓名、销假天数、事由等。

Scanner scanner= new Scanner(System.in);

System.out.println("Who are you?");
String employee = scanner.nextLine();

System.out.println("How many holidays do you want to request?");
Integer nrOfHolidays = Integer.valueOf(scanner.nextLine());

System.out.println("Why do you need them?");
String description = scanner.nextLine();


RuntimeService runtimeService = processEngine.getRuntimeService();

Map<String, Object> variables = new HashMap<String, Object>();
variables.put("employee", employee);
variables.put("nrOfHolidays", nrOfHolidays);
variables.put("description", description);

参数筹备好后,就能够传给工作流了:

ProcessInstance processInstance =
    runtimeService.startProcessInstanceByKey("holidayRequest", variables);

此时,就会依据流程定义里的:

<userTask id="approveTask" name="Approve or reject request" activiti:candidateGroups="managers"/>

创立一个工作,工作有个标签,就是 candidateGroups,这里的 managers,能够猜得出,是给 managers 建了个审批工作。

查问并审批工作

基于 manager 查问工作:

TaskService taskService = processEngine.getTaskService();
List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("managers").list();
System.out.println("You have" + tasks.size() + "tasks:");
for (int i=0; i<tasks.size(); i++) {System.out.println((i+1) + ")" + tasks.get(i).getName());
}

审批工作:

boolean approved = scanner.nextLine().toLowerCase().equals("y");
variables = new HashMap<String, Object>();
variables.put("approved", approved);
taskService.complete(task.getId(), variables);

这里就是把全局变量 approved,设为了 true,而后提交给引擎。引擎就会依据这里的变量是 true 还是 false,抉择走不同分支。如下:

<sequenceFlow sourceRef="decision" targetRef="externalSystemCall">
    <conditionExpression xsi:type="tFormalExpression">
        <![CDATA[${approved}
]]>
    </conditionExpression>
</sequenceFlow>
<sequenceFlow sourceRef="decision" targetRef="sendRejectionMail">
    <conditionExpression xsi:type="tFormalExpression">
        <![CDATA[${!approved}
]]>
    </conditionExpression>
</sequenceFlow>

回调用户代码

审批后,就会进入下一个节点:

<serviceTask id="externalSystemCall" name="Enter holidays in external system"
             activiti:class="org.example.CallExternalSystemDelegate"/>

这里有个 class,就是须要咱们本人实现的:

最初,流程就走完完结了。

REST API 模式

下面介绍的形式是其作为一个 jar,内嵌到咱们的程序里。创立引擎实例后,由咱们业务程序去驱动引擎的运行。引擎和业务代码在同一个过程里。

第二种形式,Flowable 也能够作为一个独立服务运行,提供 REST API 接口,这样的话,非 Java 语言开发的零碎就也能够应用该引擎了。

这个只须要咱们下载官网的 zip 包,外面有个 rest 的 war 包,能够间接放到 tomcat 里运行。

部署工作流

在这种形式下,如果要实现下面举例的员工销假流程,能够通过调接口来实现:

启动工作流:

其余接口就不一一展现了,能够参考官网文档。

通过页面进行流程建模

截止到目前,创立工作流程都是通过建设 xml 来实现的,这样还是十分不不便的。因而,零碎也提供了通过页面可视化的形式来创立流程,应用鼠标拖拽相应组件即可实现。

然而体验下来还是比拟辛苦的,性能很多,名词更多,有很多都不晓得是什么意思,只能一直尝试来了解。

开源 VS 自研

既然曾经有成熟的开源产品了,还须要自研吗?这算是一个陈词滥调的问题了。那到底应该如何抉择呢?其实并不艰难,归根结底就是要合乎本身的业务特点,以及理论的需要。

开源劣势:

入门门槛低,有很多能够复用的成绩。通常而言,性能比拟丰盛,周边生态也比较完善,投入产出比比拟高。一句话总结,投入少,见效快。

开源劣势:

内核不容易掌控,门槛较高,通常开源的性能和理论业务并不会齐全匹配,很多开源产品开箱即用做的不够好,须要大量调优。一句话总结,入门容易掌控难。

自研劣势:

产品核心技术掌控水平高,能够更好的贴着业务需要做,能够定制的更好,基于上述两点,通常更容易做到良好的性能体现。一句话总结,量身定制。

自研劣势:

投入产出比略低,且对团队成员的能力曲线要求较高。此外关闭的生态会导致周边反对不足,当须要一些新需要时,往往都须要定制开发。一句话总结,啥事都要靠本人。

基于以上的剖析,再联合咱们本身业务,我总结了以下几点可供参考:

  1. 开源我的项目均为 Java 技术栈,而咱们应用 Python 和 Go 比拟多,技术栈不匹配
  2. 开源我的项目功能丰富,而咱们业务绝对简略,应用起来比拟重
  3. 开源我的项目并非开箱即用,须要联合业务特点做定制开发,学习老本和保护老本比拟高

综上所述,我感觉自研更适宜咱们现阶段的产品特点。

工作流引擎架构设计

如果抉择自研,架构应该如何设计呢?有哪些比拟重要的模块和须要留神的点呢?上面来具体说说。

BPMN

BPMN 全称是 Business Process Model And Notation,即业务流程模型和符号。

能够了解成一种标准,在这个标准里,哪些地方用空心圆,哪些地方用矩形,哪些地方用菱形,都是有明确定义的。

也就是说,只有是基于这个标准开发的零碎,其所创立的流程就都是能够通用的。

其实,如果只是开发一个外部零碎,不恪守这个标准也没有问题。但要是做一个产品的话,为了通用性更强,最好还是恪守这个标准。

流程设计器

对于工作流引擎来说,流程设计器的选型至关重要,它提供了可视化的流程编排能力,决定了用户体验的好坏。

目前支流的流程设计器有 Activiti-Modeler,mxGraph,bpmn-js 等,上面来做一个简略介绍。

Activiti-Modeler

Activiti 开源版本中带了 Web 版流程设计器,在 Activiti-explorer 我的项目中有 Activiti-Modeler,长处是集成简略,开发工作量小,毛病是界面不美观,用户体验差。

mxGraph

mxGraph 是一个弱小的 JavaScript 流程图前端库,能够疾速创立交互式图表和图表应用程序,国内外驰名的 ProcessOne 和 draw.io 都是应用该库创立的弱小的在线流程图绘制网站。

因为 mxGraph 是一个凋谢的 js 绘图开发框架,咱们能够开发出很炫的款式,或者齐全依照我的项目需要定制。

官方网站:http://jgraph.github.io/mxgrap

bpmn-js

bpmn-js 是 BPMN2.0 渲染工具包和 Web 模型。bpmn-js 正在致力成为 Camunda BPM 的一部分。bpmn-js 应用 Web 建模工具能够很不便的构建 BPMN 图表,能够把 BPMN 图表嵌入到你的我的项目中,容易扩大。

bpmn-js 是基于原生 js 开发,反对集成到 vue、react 等开源框架中。

官方网站:https://bpmn.io/

以上介绍的都属于是功能强大且欠缺的框架,除此之外,还有其余基于 Vue 或者 React 开发的可视化编辑工具,大家也能够依据本人的理论需要进行抉择。

流程引擎

最初来说说流程引擎,整个零碎的外围。引擎设计的好坏决定了整个零碎的稳定性,可用性,扩展性等等。

整体架构如图所示,次要包含一下几个局部:

一、流程设计器 次要通过一系列工具创立一个计算机能够解决的工作流程形容,流程建模通常由许多离散的节点步骤组成,须要蕴含所有对于流程的必要信息,这些信息包含流程的起始和完结条件,节点之间的流转,要承当的用户工作,被调用的应用程序等。

二、流程引擎 次要负责流程实例化、流程管制、节点实例化、节点调度等。在执行过程中,工作流引擎提供流程的相干信息,治理流程的运行,监控流程的运行状态,并记录流程运行的历史数据。

三、存储服务 提供具体模型及流程流转产生的信息的存储空间,工作流零碎通常须要反对各种常见的数据库存储。

四、组织模型 不属于工作流零碎的建设范畴,但流程设计器在建模的过程中会援用组织模型,如定义工作节点的参与者。还有就是在流程流转的过程中同样也须要援用组织模型,如在进行工作指派时,须要从组织模型中确定工作的执行者。

工作流引擎外部能够应用平台本身的对立用户组织架构,也能够适配第三方提供的用户组织架构。

五、工作流引擎作为一项根底撑持服务提供给各业务零碎应用,对第三方零碎凋谢规范的 RESTful 服务

后记

上面来说说我当初开发的零碎反对到了什么水平,以及将来可能的倒退方向。因为毕竟不是一个专门的工单零碎,工单申请也只是其中的一个模块,所以在整体的性能上必定和残缺的工作流引擎有很大差距。

第一版

第一版并没有流程引擎,开发方式简略粗犷,每减少一个流程,就须要从新开发对应的表和业务代码。

这样做的毛病是非常明显的:

  1. 每个流程须要独自开发,工作量大,开发效率低
  2. 流程性能相近,代码反复量大,冗余,不利于保护
  3. 定制化开发,短少扩展性 #

第二版

第二版,也就是目前的版本。

随着工单流程逐步增多,工作量逐步增大,于是开始对流程进行优化,开发了现阶段的工作流引擎。

在新增一个工单流程时,须要先进行工作流配置,配置其根底信息,自定义字段,状态和流转这些信息。还反对配置自动化节点,能够依据条件由程序主动实现相干操作并审批。

配置好之后,后端无需开发,由对立的引擎代码进行解决,包含节点审批流转,状态变更等。只须要开发前端的创立和查问页面即可,相比于第一版,曾经在很大水平上进步了开发效率。

目前版本须要优化的点:

  1. 短少可视化流程设计器,无奈做到拖拽式设计流程
  2. 节点之间状态流转不够灵便
  3. 短少分布式事物反对,以及异样解决机制

下一个版本

针对以上有余,下一个版本筹备次要优化三点,如下:

  1. 须要反对可视化流程设计器,使流程设计更加简略,灵便
  2. 依据流程配置主动生成前端页面,做到新增一种类型的工单,无需开发
  3. 减少节点自动化能力,异样解决机制,进步零碎的稳定性

以上就是本文的全部内容,如果感觉还不错的话欢送 点赞 转发 关注,感激反对。


参考文章:

  • https://www.cnblogs.com/grey-…
  • https://www.cnblogs.com/duck-…
  • https://zhuanlan.zhihu.com/p/…
  • https://zhuanlan.zhihu.com/p/…
  • https://bbs.qolome.com/?p=365
  • https://workflowengine.io/blo…

举荐浏览:

  • Git 分支管理策略

正文完
 0