Camunda ServiceTask

时间:2024-01-22 16:36:06


一:Java class

Java class实现JavaDelegate接口,只需要配置类的全限定名即可,不需要被Spring容器管理。

Camunda ServiceTask_python

public class JavaClassServiceTask implements JavaDelegate {
    @Override
    public void execute(DelegateExecution execution) throws Exception {
        System.out.println("JavaClassServiceTask");
        String currentActivityId = execution.getCurrentActivityId();
        execution.setVariable(currentActivityId, "ok");
    }
}

二:Delegate expression

实现JavaDelegate接口,配置成Bean的名字即可。

Camunda ServiceTask_xml_02

@Service
public class DelegateExpressionServiceTask implements JavaDelegate {
    @Override
    public void execute(DelegateExecution execution) throws Exception {
        System.out.println(execution.getVariable("JavaClass"));
        System.out.println("DelegateExpressionServiceTask");
    }
}

三:Expression

配置成普通的方法调用,可以是任意类的任意方法,方法的返回值会赋值给Result variable变量,该变量可以从下一个节点获取。

Camunda ServiceTask_serviceTask_03

@Service
public class ExpressionServiceTask {

    public boolean execute(DelegateExecution execution) {
        System.out.println("ExpressionServiceTask");
        return true;
    }
}

四:External

Camunda ServiceTask_python_04

@Configuration
public class CamundaHandlerConfig {

    @Bean
    @ExternalTaskSubscription(topicName = "java_external_task_topic",
            processDefinitionKeyIn = {"ServiceTaskProcess"},
            lockDuration = 5000)
    public ExternalTaskHandler externalTaskHandler() {
        return (ExternalTask externalTask, ExternalTaskService externalTaskService) -> {
            boolean expressionResult = (boolean)externalTask.getVariable("expressionResult");
            // 处理业务逻辑...
            if (!expressionResult) {
                externalTaskService.handleFailure(externalTask, "error msg...", "error detail...", 0, 5000);
            }
            Map<String, Object> variables = new HashMap<>();
            variables.put("result", 1);
            externalTaskService.complete(externalTask, variables);
        };
    }
}

Camunda ServiceTask_System_05

# pip3 install camunda-external-task-client-python3
# pip3 install pydantic

from camunda.external_task.external_task import ExternalTask, TaskResult
from camunda.external_task.external_task_worker import ExternalTaskWorker

default_config = {
    "maxTasks": 1,  # 一次只拉一个任务,这样多实例处理就不会所有任务被一个实例锁住
    "lockDuration": 10000,  # 锁任务的时间
    "asyncResponseTimeout": 30000,
    "retries": 0,
    "retryTimeout": 5000,
    "sleepSeconds": 30000,  # 每次拉取的间隔时间
    "auth_basic": {"username": "admin", "password": "123456"}
}


def handle_task(task: ExternalTask) -> TaskResult:
    result = task.get_variable("result")
    if result:
        return task.failure(error_message="error_message",
                            error_details="error_details 如异常堆栈信息",
                            max_retries=0, retry_timeout=5000)
    return task.complete()


if __name__ == '__main__':
    worker = ExternalTaskWorker(worker_id="python-client",
                                base_url="http://localhost:8080/engine-rest",
                                config=default_config).subscribe("python_external_task_topic", handle_task)

五:运行

  1. 启动工作流引擎
  2. 启动Java客户端
  3. 启动Python客户端
repositoryService.createDeployment()
       .name("服务任务流程")
       .addClasspathResource("bpmn/service_task.bpmn")
       .deploy();

identityService.setAuthenticatedUserId("huihui");
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("ServiceTaskProcess");
System.out.println("processInstance=" + processInstance.getId());

Camunda ServiceTask_python_06


Camunda ServiceTask_xml_07

Camunda ServiceTask_System_08

五:连接器 Connector

连接器Connector就是内置apache的http client 就是在执行这个节点时发送一个http请求,对结果进行响应,感觉非常鸡肋的功能,我用UserTask或者其他ServiceTask在代码里调用不香吗,我可以在代码中写复杂的逻辑,组装复杂的请求参数,打印日志,记录请求结果等其它操作,这玩意也就适用最简单的方法调用,有大量参数时光组装参数就没法玩。

5.1 pom.xml

<dependency>
   <groupId>org.camunda.bpm</groupId>
   <artifactId>camunda-engine-plugin-connect</artifactId>
   <version>${camunda.spring-boot.version}</version>
</dependency>
<dependency>
   <groupId>org.camunda.connect</groupId>
   <artifactId>camunda-connect-connectors-all</artifactId>
   <version>1.5.6</version>
</dependency>


<dependency>
    <groupId>org.camunda.bpm</groupId>
    <artifactId>camunda-engine-plugin-spin</artifactId>
    <version>${camunda.spring-boot.version}</version>
</dependency>
<dependency>
    <groupId>org.camunda.spin</groupId>
    <artifactId>camunda-spin-dataformat-json-jackson</artifactId>
    <version>1.17.0</version>
</dependency>

5.2 config

public class HttpConnectorConfigurator implements ConnectorConfigurator<HttpConnector> {
    public Class<HttpConnector> getConnectorClass() {
        return HttpConnector.class;
    }

    public void configure(HttpConnector connector) {
        CloseableHttpClient client = HttpClients.custom()
                .setMaxConnPerRoute(10)
                .setMaxConnTotal(200)
                .build();
        ((AbstractHttpConnector) connector).setHttpClient(client);
    }
}

5.3 SPI

在文件src/main/resources/META-INF/services/org.camunda.connect.spi.ConnectorConfigurator中配置以下完全限制类。

com.example.camunda.config.HttpConnectorConfigurator

5.3 Controller

@RestController
public class UserController {

    @GetMapping("/user/{userId}")
    public Map<String, Object> user(@PathVariable("userId") String userId) {
        Map<String, Object> map = new HashMap<>();
        map.put("code", "200");
        map.put("result", "success");
        map.put("data", userId);
        return map;
    }
}

5.5 bpmn

Camunda ServiceTask_System_09

Camunda ServiceTask_xml_10


Camunda ServiceTask_python_11

Camunda ServiceTask_serviceTask_12

<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_042u8vr" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.18.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.20.0">
  <bpmn:process id="ConnectorProcess" name="HttpClient服务" isExecutable="true" camunda:historyTimeToLive="180">
    <bpmn:startEvent id="StartEvent_1" camunda:initiator="userId">
      <bpmn:outgoing>Flow_1q9dey8</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:sequenceFlow id="Flow_1q9dey8" sourceRef="StartEvent_1" targetRef="Activity_1nr3626" />
    <bpmn:serviceTask id="Activity_1nr3626" name="http-connector">
      <bpmn:extensionElements>
        <camunda:connector>
          <camunda:inputOutput>
            <camunda:inputParameter name="headers">
              <camunda:map>
                <camunda:entry key="Content-Type">application/json</camunda:entry>
              </camunda:map>
            </camunda:inputParameter>
            <camunda:inputParameter name="method">GET</camunda:inputParameter>
            <camunda:inputParameter name="url">http://localhost:8081/user/${userId}</camunda:inputParameter>
            <camunda:outputParameter name="response">
              <camunda:script scriptFormat="JavaScript">var res = connector.getVariable("response");
res;</camunda:script>
            </camunda:outputParameter>
          </camunda:inputOutput>
          <camunda:connectorId>http-connector</camunda:connectorId>
        </camunda:connector>
      </bpmn:extensionElements>
      <bpmn:incoming>Flow_1q9dey8</bpmn:incoming>
      <bpmn:outgoing>Flow_0bklhmm</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:sequenceFlow id="Flow_0bklhmm" sourceRef="Activity_1nr3626" targetRef="UserTask1" />
    <bpmn:userTask id="UserTask1" name="UserTask1">
      <bpmn:incoming>Flow_0bklhmm</bpmn:incoming>
      <bpmn:outgoing>Flow_1w1qnew</bpmn:outgoing>
    </bpmn:userTask>
    <bpmn:endEvent id="end">
      <bpmn:incoming>Flow_1w1qnew</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="Flow_1w1qnew" sourceRef="UserTask1" targetRef="end" />
  </bpmn:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="ConnectorProcess">
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="179" y="99" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_1npa7km_di" bpmnElement="Activity_1nr3626">
        <dc:Bounds x="270" y="77" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_1neh381_di" bpmnElement="UserTask1">
        <dc:Bounds x="430" y="77" width="100" height="80" />
        <bpmndi:BPMNLabel />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_199n12t_di" bpmnElement="end">
        <dc:Bounds x="592" y="99" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="Flow_1q9dey8_di" bpmnElement="Flow_1q9dey8">
        <di:waypoint x="215" y="117" />
        <di:waypoint x="270" y="117" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0bklhmm_di" bpmnElement="Flow_0bklhmm">
        <di:waypoint x="370" y="117" />
        <di:waypoint x="430" y="117" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1w1qnew_di" bpmnElement="Flow_1w1qnew">
        <di:waypoint x="530" y="117" />
        <di:waypoint x="592" y="117" />
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn:definitions>

5.6 test

repositoryService.createDeployment()
       .name("HttpClient服务")
       .addClasspathResource("bpmn/connector.bpmn")
       .deploy();

identityService.setAuthenticatedUserId("huihui");
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("ConnectorProcess");

5.7 db

Camunda ServiceTask_xml_13