文章目录
一、camunda基础
Camunda介绍 Camunda是一种工作流引擎,是由Java开发的一个纯Java库。
通常集成在我们的服务中,作为其中一块功能。
1.1 安装与部署流程引擎
- Camunda Platform 和 Camunda Modeler是必要的文件。
- Camunda Platform:这里是流程引擎的用户、租户等部分的管理界面
- Camunda Modeler:流程引擎的核心,即流程相关的部分
- docker安装
docker pull camunda/camunda-bpm-platform:run-latest
docker run -d --name camunda -p 8080:8080 camunda/camunda-bpm-platform:run-latest
- 非docker安装
我们下载好后,需要到这个路径下。这里是创建表的sql语句,选择自己的数据库运行语句,来创建对应的表。
修改default.yml 中数据源和JDBC 替换为自己使用的数据库的配置
然后把对应的JDBC放入标注上面的userlib之中即可。
Camunda Modeler 下载地址
我们在点击运行后可以直接使用。
1.2 流程引擎结构
1.3 流程引擎的基本使用
我们创建在Camunda Modeler中创建一个流程,然后我们可以用Java或者JS,按照流程去执行它。
本篇使用Java处理。
1.3.1 创建一个BPMN Diagram
首先,我们可以创建一个BPMN Diagram。
Business Process Modeling Notation,简称BPMN
它的事件包含
- 开始(Start)
- 中间(Intermediate)
- 边界(Boundary)
- 结束(End)
根据触发方式不同,可以分为
- 捕获事件(Catching Event)
- 抛出事件(Throwing Event)
具体的使用方式请参考:参考-Executing automated steps 、Service tasks
我们准备一个简易的流程,如下图(与官方例子一致)。
这里可以直接下载官方的例子下载避免繁琐的配置操作
注意一下,最新版的是可集群的版本(版本8)
我们使用的是如下的版本(版本7)
1.3.2 实现一个外部工作者
换言之就是创建一个Java程序,用它以处理BPMN Diagram
这里是官方的写的依赖,依据自己的需要改动即可。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.camunda.bpm.getstarted</groupId>
<artifactId>charge-card-worker</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<camunda.external-task-client.version>7.17.0</camunda.external-task-client.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.camunda.bpm</groupId>
<artifactId>camunda-external-task-client</artifactId>
<version>${camunda.external-task-client.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
</dependencies>
</project>
笔者改为Spring Boot(Java11)
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.6</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
<groupId>org.camunda.bpm</groupId>
<artifactId>camunda-external-task-client</artifactId>
<version>7.18.0</version>
</dependency>
</dependencies>
该程序连接的是 Camunda Platform 系统注意端口和IP。
import lombok.extern.slf4j.Slf4j;
import org.camunda.bpm.client.ExternalTaskClient;
import java.awt.*;
import java.net.URI;
@Slf4j
public class ChargeCardWorker {
public static void main(String[] args) {
ExternalTaskClient client = ExternalTaskClient.create()
// 依据实际情况填写程序的IP和端口
.baseUrl("http://localhost:8080/engine-rest")
// 异步相应超时时间
.asyncResponseTimeout(10000) // long polling timeout
.build();
// subscribe to an external task topic as specified in the process
// 订阅流程中指定的外部任务主题,与活动ID对应
client.subscribe("charge-card")
// 默认锁的持续时间为20秒
.lockDuration(1000) // the default lock duration is 20 seconds, but you can override this
// 处理内容
// externalTask:任务,externalTaskService:处理服务
.handler((externalTask, externalTaskService) -> {
// Put your business logic here
// Get a process variable
String item = externalTask.getVariable("item");
Integer amount = externalTask.getVariable("amount");
log.info("Charging credit card with an amount of '{}'€ for the item '{}'...",amount,item);
try {
// 打开页面并挂起
Desktop.getDesktop().browse(new URI("https://docs.camunda.org/get-started/quick-start/complete"));
} catch (Exception e) {
e.printStackTrace();
}
// Complete the task
// 任务完成
externalTaskService.complete(externalTask);
})
.open();
}
}
这里的活动的Topic与我们的工作者一致。因而可以知道活动有对应的工作者去处理。
通过改变活动以及活动的顺序来改变流程,而活动自己被不同的工作者处理后继续流转因而不受影响,以此完成流程。
1.3.3 部署流程
也就是将BPMN Diagram 与 Platform关联,然后让我们的工作者去处理它。
我们将流程部署过去
成功后,我们进入platform登陆后(默认账号密码均为demo),可以看到流程已经在这里了
http://localhost:8080/camunda/app/cockpit/default/#/processes
1.3.4 创建一个流程实例并消费
我们接下来可以使用postman
向这个地址发送Post请求
http://localhost:8080/engine-rest/process-definition/key/payment-retrieval/start
同时在body里面发送如下json参数
{
"variables": {
"amount": {
"value":555,
"type":"integer"
},
"item": {
"value": "item-xyz"
}
}
}
当我们在工作者的后台看到如下数据,并且成功打开了一个页面,就说明成功消费了
此处的key就是我们process的ID:payment-retrieval
1.3.5 向流程中添加用户任务
当然这里只是最简单的使用。参考-Add a User Task to the Process
依据类似的操作,我们添加一个
我们把任务分配给demo用户
下面的候选组、人、到期日期请自行参考文档
我们接着在这个活动的Forms添加如下属性,来向流程中加一个表单
选择 File > New File > Form(版本7)创建如下表单
并把ID和我们payment-form设置为一样的,来关联起来。
我们接下来创建一个Amount项目,使用Number类型
再创建一个文本框,如下设置
再加一个是否同意
完成回到流程然后部署,记得加入我们的表单
来到这个页面,点击框住的按钮
http://localhost:8080/camunda/app/tasklist/
选择我们创建的流程,开启一个流程
输入一个业务流程号后,我们可以找到我们的流程以及表格
同时可以看到流程位置
填写完成后,我们继续,则会流转到我们前面写的消费者处。
1.3.6 添加网关
也就是类似if-else的方式,来确定流向。
我们先搭出如下内容
网关1
- 如果金额小于 1000 就直接处理
- 如果大等于 1000 就需要处理
网关2
- 判断是否同意
我们的判断是加在箭头上的
接下来我们部署该流程,按照1.3.5中流程开始一个流程,或者通过PostMan请求,可以看到流程流转到了不同地方。
platform 创建流程
我们查看,可以看到流程到了这里
启动我们的外部工作者,即可完成处理。
postman 创建流程
发送的也是和之前一样的请求
可以看到我们这里的流程到了1.3.5的部分,在tasklist中我们处理是否同意即可。
1.3.7 业务规则
我们在这里加入业务规则
随后我们创建一个新的DMN流程(依然是版本7)。
我们这里直接把Decision1的ID(决策表)和我们业务规则输入的reference关联起来
点击框住部分
创建如下内容
标题我们是Item大写,Expression我们是小写让变量与之后流程一致
制作如下表格
到现在为止,我们完成了决策表的制作。
- 如果我们的item是"item-xyz"决策就是赞成
- 若果不是,则结果就是不赞成
关于决策表,可以参考Camunda DMN
我们接下来把它部署即可,名字可以取框住的
随后我们可以在决策里面找到
在重新部署我们的流程
然后我们开始流程
二、Java 集成流程引擎
我们用SpringBoot集成流程引擎。
相关的SpringBoot配置可以参考-流程引擎配置
2.1 为项目引入Platform
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<camunda.spring-boot.version>7.18.0</camunda.spring-boot.version>
<spring-boot.version>2.7.3</spring-boot.version>
<xml-bind.version>2.3.6</xml-bind.version>
<maven.compiler.target>11</maven.compiler.target>
<mybatis-plus.version>3.5.1</mybatis-plus.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.camunda.bpm.springboot</groupId>
<artifactId>camunda-bpm-spring-boot-starter</artifactId>
<version>${camunda.spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.camunda.bpm.springboot</groupId>
<artifactId>camunda-bpm-spring-boot-starter-webapp</artifactId>
<version>${camunda.spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.camunda.bpm.springboot</groupId>
<artifactId>camunda-bpm-spring-boot-starter</artifactId>
<version>${camunda.spring-boot.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
yml
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://IP:Port/DataBase?useUnicode=true&NamePatternMatchesAll=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=true&useSSL=false
username: username
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
application:
name: applicationName
camunda:
bpm:
admin-user:
id: demo
password: demo
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
package camunda;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class CamundaApplication {
public static void main(String[] args){
SpringApplication.run(CamundaApplication.class,args);
}
}
在数据库中我们可以看到我们配置的默认用户。
启动后我们访问
http://localhost:8080/camunda/app/welcome/default/#!/login
出现如下页面就说明成功了。
页面和地址的使用方式和我们之前的使用方式是一致的。
2.1.1 页面配置
有页面是因为我们引入了,不需要页面的时候,我们可以不引入它即可。
<dependency>
<groupId>org.camunda.bpm.springboot</groupId>
<artifactId>camunda-bpm-spring-boot-starter-webapp</artifactId>
<version>${camunda.spring-boot.version}</version>
</dependency>
此外,我们可以配置yml,来修改路径以及是否重定向到默认的index.html.
camunda:
bpm:
webapp:
application-path: 路径
index-redirect-enabled: false
2.2 简单使用
可以参考-Camunda BPM Javadocs 7.9.19-ee
2.2.1 引入API
我们可以引入如下依赖:
<dependency>
<groupId>org.camunda.bpm.springboot</groupId>
<artifactId>camunda-bpm-spring-boot-starter-rest</artifactId>
<version>${camunda.spring-boot.version}</version>
</dependency>
关于是否需要这个扩展包,new bing 给出的答复如下:
Camunda BPM Spring Boot Starter是一个Camunda BPM的Spring Boot Starter,它提供了REST API的支持,可以让您的Camunda BPM应用程序更容易地与其他应用程序集成。
- 使用camunda-bpm-spring-boot-starter-rest扩展,您可以使用Camunda BPM REST API与Camunda BPM引擎进行交互。这使得您可以使用Camunda BPM REST API执行各种操作,例如启动流程实例、查询任务、完成任务等。
- 如果您不使用camunda-bpm-spring-boot-starter-rest扩展,您仍然可以使用Camunda BPM REST API与Camunda BPM引擎进行交互。但是,您需要手动配置REST API,并且需要编写代码来与REST API进行交互。
也就是REST API 让我们可以用PostMan等方式,直接调用接口。不用的话,我们则可以自己写接口。
它的使用可参考-REST API Reference
默认API地址为
http://localhost:8080/engine-rest
Camunda使用的是jersey,因而一些配置需要jersey配置方式。
也可以通过修改spring boot的通用应用程序属性来改变API访问地址:
spring:
jersey:
application-path=地址
为了修改配置或注册额外的资源,可以提供一个集成自如下的配置类:
org.camunda.bpm.spring.boot.starter.rest.CamundaJerseyResourceConfig
@Component
@ApplicationPath("/engine-rest")
public class JerseyConfig extends CamundaJerseyResourceConfig {
@Override
protected void registerAdditionalResources() {
register(...);
}
}
中心起点是ProcessEngine(流程引擎),可以通过配置部分中描述的几种方式创建ProcessEngine。
从ProcessEngine中,可以获得包含 工作流/BPM 方法的各种服务。
ProcessEngine和服务对象是 线程安全 的。所以可以为整个服务器保留对其中一个的引用。
actiti.cfg.xml文件:
- 对于所有的actiti.cfg.xml文件,流程引擎将以Spring方式构建:首先创建Spring应用程序上下文,然后从该应用程序上下文获得流程引擎。
- 如何书写,可参考-activiti配置文件activiti.cfg.xml
服务都是无状态的:
- 可以在集群中的多个节点上运行Camunda BPM,每个节点都访问同一个数据库,而不必担心哪台机器实际执行了前面的调用。对任何服务的任何调用都是幂等的,无论它在哪里执行。
2.2.2 服务
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
将在第一次调用流程引擎时初始化并构建流程引擎,之后总是返回相同的流程引擎.
可以使用ProcessEngines.init()和ProcessEngines.destroy()来正确地创建和关闭所有的流程引擎
RepositoryService repositoryService = processEngine.getRepositoryService();
RepositoryService可能是使用Camunda引擎时需要的第一个服务。此服务提供用于管理和操作部署和流程定义的操作。
部署 部署内容 意味着将其上传到引擎,在将所有进程存储到数据库之前,都将会对其进行检查和解析。
完成后,系统就知道部署了,之后就可以启动部署中包含的任何进程。
通过该服务,可以:
- 查询引擎已知的部署和流程定义。
- 挂起并激活流程定义。挂起意味着不能对它们进行进一步的操作,而激活则是相反的操作。
- 检索引擎自动生成的部署图或流程图中包含的文件等各种资源
RuntimeService runtimeService = processEngine.getRuntimeService();
RuntimeService处理启动流程定义的新流程实例。
流程定义定义了流程中不同步骤的结构和行为,流程实例是此类流程定义的一次执行。对于每个流程定义,通常都有多个实例同时运行。
RuntimeService也是用于检索和存储流程变量的服务。这是特定于给定流程实例的数据,可以由流程中的各种构造使用(例如,独占网关通常使用流程变量来确定选择哪条路径来继续流程)。
RuntimeService还允许对流程实例和执行进行查询。基本上,执行就是指向流程实例当前位置的指针。
最后,当流程实例等待外部触发器且流程需要继续时,将使用RuntimeService。流程实例可以有各种等待状态,该服务包含各种“通知”实例接收到外部触发器并可以继续该流程实例的操作。
TaskService taskService = processEngine.getTaskService();
需要展示给实际用户执行的任务是流程引擎的核心。
围绕任务的所有内容都分组在TaskService中,例如
- 查询已分配给用户/组的任务。
- 创建新的独立任务。这些任务与流程实例无关。
- 操作将任务分配给哪个用户,或者哪些用户以某种方式参与了任务。
- 申请并完成一项任务。声明意味着某人决定成为任务的受让人,这意味着该用户将完成任务。完成的意思是“完成任务的工作”。这通常是填写某种表单。
IdentityService identityService = processEngine.getIdentityService();
IdentityService允许对组和用户进行管理(创建、更新、删除、查询等)。
但是,核心引擎在运行时实际上不会对用户进行任何检查。
FormService formService = processEngine.getFormService();
FormService是一个可选服务。
此服务引入了启动表单和任务表单的概念。
- 启动表单是在流程实例启动之前向用户显示的表单
- 任务表单是在用户希望完成表单时显示的表单
该服务以一种易于使用的方式公开此数据。但同样,这是可选的,因为表单不需要嵌入到流程定义中。
HistoryService historyService = processEngine.getHistoryService();
HistoryService公开引擎收集的所有历史数据。
在执行流程时,引擎可以保存大量数据(这是可配置的),例如流程实例的开始时间、谁执行了哪些任务、完成任务所需的时间、每个流程实例遵循的路径等等。
该服务主要提供访问此数据的查询功能。
ManagementService managementService = processEngine.getManagementService();
在编写自定义应用程序时,通常不需要ManagementService。
它允许检索关于数据库表和表元数据的信息。此外,它还公开了作业的查询功能和管理操作。作业在引擎中用于各种用途,如计时器、异步延续、延迟挂起/激活等。
FilterService filterService = processEngine.getFilterService();
FilterService允许创建和管理过滤器。过滤器是像任务查询一样存储的查询。
例如,Tasklist使用过滤器来过滤用户任务。
ExternalTaskService externalTaskService = processEngine.getExternalTaskService();
ExternalTaskService提供对外部任务实例的访问。外部任务表示在外部独立于流程引擎处理的工作项。
CaseService caseService = processEngine.getCaseService();
CaseService类似于RuntimeService,但用于案例实例。它处理启动用例定义的新用例实例和管理用例执行的生命周期。该服务还用于检索和更新案例实例的流程变量。
DecisionService decisionService = processEngine.getDecisionService();
DecisionService允许评估部署到引擎的决策。它是在独立于流程定义的业务规则任务中评估决策的一种替代方法。
2.2.3 部署流程
我们在启动类上添加@EnableProcessApplication注释,并将空的processes.xml文件放在src/main/resources/META-INF文件夹中。
@SpringBootApplication
@EnableProcessApplication
public class CamundaApplication {
public static void main(String[] args){
SpringApplication.run(CamundaApplication.class,args);
}
}
Camunda Engine的每个流程应用程序都需要该文件,此处我们始终将它保持为空(它会使用默认配置)
这里我们仍然从Modeler处创建一个流程。
然后保存在如下位置
启动后,我们可以看到已经成功部署
在流程定义中可以找到
此处也可以找到
如下就是部署与移除的流程,默认的配置下,即使删除bpmn,流程仍然存在于数据库中
我们可以创一个用于创建实例的类,这里的key就是我们流程的key,至此该监听器监听该流程。
可以完成部署后立即创建一个实例(重启也会)
@Service
@Slf4j
public class TestCamunda {
@Autowired
private RuntimeService runtimeService;
@EventListener
private void processPostDeploy(PostDeployEvent event) {
log.info("{}",event);
runtimeService.startProcessInstanceByKey("Process_028ntv2");
}
}
可以在这个位置找到
我们可以做一个接口,通过接受服务ID,然后用RuntimeService去创建。
消费服务我们之前就写给过了,这里就跳过。
2.3 流程部署详细介绍
默认情况下,camunda-spring-boot-starter 使用SpringProcessEngineConfiguration 配置自动部署功能。
从1.2.0开始,可以通过 SpringBootProcessApplication 配置。这将禁用SpringProcessEngineConfiguration 的自动部署功能。
自动部署功能,用如下路径作为资源扫描的目录。
META-INF/processes.xml
允许使用的所有 processes.xml 配置项在这里列出。
如果是空的,就会用默认配置,如下:
<process-application
xmlns="http://www.camunda.org/schema/1.0/ProcessApplication"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<process-archive>
<properties>
<property name="isDeleteUponUndeploy">false</property>
<property name="isScanForProcessDefinitions">true</property>
</properties>
</process-archive>
</process-application>
官方给出的配置样例:
<process-application
xmlns="http://www.camunda.org/schema/1.0/ProcessApplication"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<process-archive name="loan-approval">
<process-engine>default</process-engine>
<properties>
<property name="isDeleteUponUndeploy">true</property>
<property name="isScanForProcessDefinitions">true</property>
</properties>
</process-archive>
</process-application>
我们在这里声明了一个单个部署(流程存档),流程存档的名称为loan-approval,并部署到名称为default的流程引擎。
-
isDeleteUponUndeploy:此属性控制流程应用程序的取消部署是否需要从数据库中删除流程引擎部署。
- 默认设置为false。
- 如果将此属性设置为true,则取消部署流程应用程序将导致从数据库中删除部署(包括流程实例)。
-
isScanForProcessDefinitions:如果此属性设置为true,则会自动扫描流程应用程序的类路径以查找可部署资源。
配置文件的基本设置,可参考-The processes.xml Deployment Descriptor
配置参数可查阅
只需要添加@EnableProcessApplication注解到SpringBootapplication类即可:
@SpringBootApplication
@EnableProcessApplication
public class CamundaApplication{
}
由于使用@EnableProcessApplication时,没有扩展ProcessApplication类,所以我们不能使用@PostDeploy和@PreUndeploy方法注释。
相反,这些回调是通过Spring事件发布机制提供的。所以可以使用以下事件监听器。
@SpringBootApplication
@EnableProcessApplication
public class CamundaApplication{
//...
}
@EventListener
public void onPostDeploy(PostDeployEvent event) {
//...
}
@EventListener
public void onPreUndeploy(PreUndeployEvent event) {
//...
}
通过监听器,我们可以完成如下操作。