# 服务任务
服务任务是业务流程自动化中的核心执行单元,用于实现无需人工干预的自动化操作。当流程流转到一个服务任务节点时,引擎会自动执行您预先定义好的业务逻辑,例如调用一个微服务接口、计算数据、发送消息或更新数据库等。
# 实现方式
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即可。
解耦与伸缩:业务逻辑服务与引擎独立部署、扩展和维护,互不影响。
容错性:即使工作者全部宕机,流程引擎也不会崩溃,任务会安静地等待工作者恢复。