# 服务任务

服务任务是业务流程自动化中的​​核心执行单元​​,用于实现无需人工干预的自动化操作。当流程流转到一个服务任务节点时,引擎会自动执行您预先定义好的业务逻辑,例如调用一个微服务接口、计算数据、发送消息或更新数据库等。

foo

# 实现方式

Activit, Flowable,Camunda三大工作流引擎都支持服务任务,在具体实现细节和性能有所区别。接下来以功能最为丰富的Camunda进行讲解。

# 内部实现

这类方式适用于业务逻辑与流程引擎部署在​​同一应用内​​的场景,执行通常是同步的。

Java委托类(Java Delegate) 这是最常用、类型最安全的方式。你需要创建一个Java类,实现特定的接口(如Camunda的JavaDelegate接口),并将业务逻辑写在execute方法中

// 以Camunda为例
public class CalculateInterestService implements JavaDelegate {
    @Override
    public void execute(DelegateExecution execution) throws Exception {
        // 从流程变量中获取数据
        Double principal = (Double) execution.getVariable("loanAmount");
        Double rate = (Double) execution.getVariable("interestRate");
        // 执行业务计算
        Double interest = principal * rate;
        // 将结果存回流程变量
        execution.setVariable("calculatedInterest", interest);
    }
}

在BPMN XML中,通过camunda:class属性指定这个类的全限定名

<serviceTask id="calculateInterestTask" name="计算利息" 
             camunda:class="com.example.CalculateInterestService" />

委托表达式(Delegate Expression)​ 提供了更大的灵活性,允许你使用表达式(如Spring Bean的名称)动态指定委托对象

//bpmn节点的参数设置
<serviceTask id="calculateInterestTask" name="计算利息" 
             camunda:delegateExpression="${eventBaseTask}" />
//java代码实现
import lombok.extern.slf4j.Slf4j;
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;

@Component("eventBaseTask")
@Slf4j
public class EventBaseTask implements JavaDelegate {
    @Override
    public void execute(DelegateExecution delegateExecution) throws Exception {
        log.info("事件网关服务任务:{}", delegateExecution.getVariable("complain"));
        log.info("事件网关服务任务processInstanceId:{}", delegateExecution.getProcessInstanceId());
    }
}

这种方式非常适合与依赖注入框架(如Spring)集成,委托对象可以被IoC容器管理。

表达式(Expression)​ 适用于非常简单的逻辑,例如调用一个已有Bean的某个方法。它不需要创建专门的委托类

<serviceTask id="logResultTask" name="记录结果" 
             camunda:expression="${loggerService.logResult(execution)}" />

# 外部任务

这是Camunda的一项​​特色且强大​​的功能,Flowable和Activiti没有对等的原生支持 。
其核心思想是​​解耦​​:流程引擎创建任务并放入一个“任务列表”,而真正的工作者(Worker)可以是一个独立于引擎的、甚至是用不同语言编写的应用程序,它通过轮询API来获取并完成任务。

  • 工作原理

​​1.创建任务​​:流程执行到外部任务节点时,引擎将其持久化到数据库(如ACT_RU_EXT_TASK表),并指定一个主题(Topic)。
​2.​获取与锁定​​:外部工作者(Worker)定期调用引擎的REST或Java API,请求获取某个主题的任务。获取成功后,该任务会被锁定一段时间,防止被其他工作者重复获取。
​3.​完成任务​​:工作者执行完业务逻辑后,回调引擎的API告知任务完成或失败,引擎据此继续推进流程。

  • bpmn配置
<serviceTask id="validateAddressTask" name="验证地址"
             camunda:type="external"
             camunda:topic="addressValidation" />
  • 外部工作者
@Component
public class AddressValidationWorker {
    @PostConstruct
    public void subscribe() {
        ExternalTaskClient client = ExternalTaskClient.create()
            .baseUrl("http://camunda-engine:8080/engine-rest")
            .build();
        client.subscribe("addressValidation")
            .lockDuration(20000)
            .handler((externalTask, externalTaskService) -> {
                // 1. 从任务中获取所需变量
                String address = (String) externalTask.getVariable("deliveryAddress");
                // 2. 执行业务逻辑(如调用地址校验服务)
                boolean isValid = validateAddress(address);
                // 3. 完成任务,并可设置新的流程变量
                Map<String, Object> variables = Variables.createVariables().put("isAddressValid", isValid);
                externalTaskService.complete(externalTask, variables);
            }).open();
    }
}
  • 外部任务的优势​​:
    技术异构​​:工作者可以用任何语言编写(Python, Go, .NET等),只需能调用REST API即可。
    ​​解耦与伸缩​​:业务逻辑服务与引擎独立部署、扩展和维护,互不影响。
    ​​容错性​​:即使工作者全部宕机,流程引擎也不会崩溃,任务会安静地等待工作者恢复。