Camunda v7
Основные концепции
Process Engine (Движок процессов)
Назначение: Ядро Camunda, которое выполняет BPMN процессы, управляет состоянием процессов и координирует выполнение задач.
Ключевые компоненты:
- Process Definition - описание процесса в BPMN формате
- Process Instance - конкретный экземпляр выполняемого процесса
- Activity Instance - состояние выполнения конкретной активности
- Execution - указатель на текущую позицию в процессе
// Получение Process Engine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = processEngine.getRuntimeService();
TaskService taskService = processEngine.getTaskService();
// Запуск процесса
ProcessInstance instance = runtimeService.startProcessInstanceByKey(
"orderProcess",
Map.of("orderId", "12345", "amount", 1000)
);
BPMN элементы
Start Event - точка начала процесса, может быть триггером по времени, сообщению или событию.
Service Task - автоматически выполняемая задача, реализуется через Java класс или внешний сервис.
User Task - задача, требующая человеческого участия, появляется в task list.
Gateway - узел принятия решений:
- Exclusive Gateway - выбирает один путь из нескольких
- Parallel Gateway - разделяет поток на параллельные ветки
- Inclusive Gateway - может активировать несколько путей
// Service Task Implementation
@Component
public class OrderValidator implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {
String orderId = (String) execution.getVariable("orderId");
// Валидация заказа
boolean isValid = validateOrder(orderId);
execution.setVariable("orderValid", isValid);
}
}
Deployment и Process Definition
Deployment (Развертывание)
Процесс: Загрузка BPMN файлов в Process Engine, создание Process Definition из BPMN диаграммы.
Способы развертывания:
- Автоматическое через
processes.xml
и@EnableProcessApplication
- Программное через
RepositoryService
- Через REST API или Camunda Admin
// Программное развертывание
@PostConstruct
public void deployProcess() {
repositoryService.createDeployment()
.addClasspathResource("processes/order-process.bpmn")
.name("Order Process Deployment")
.deploy();
}
// Получение Process Definition
ProcessDefinition definition = repositoryService
.createProcessDefinitionQuery()
.processDefinitionKey("orderProcess")
.latestVersion()
.singleResult();
Versioning (Версионирование)
Принцип: Каждый deployment создает новую версию Process Definition. Активные процессы продолжают выполняться на старой версии.
Управление версиями:
- Новые процессы запускаются на последней версии
- Можно принудительно запустить на конкретной версии
- Миграция процессов между версиями возможна
// Запуск на конкретной версии
runtimeService.startProcessInstanceById(
"orderProcess:2:12345" // processDefinitionId с версией
);
// Миграция активных процессов
MigrationPlan migrationPlan = runtimeService
.createMigrationPlan("orderProcess:1:123", "orderProcess:2:456")
.mapActivities("oldTaskId", "newTaskId")
.build();
runtimeService.newMigration(migrationPlan)
.processInstanceIds(processInstanceIds)
.execute();
Variables и Scope
Process Variables (Переменные процесса)
Назначение: Хранение данных процесса, доступных на всех уровнях выполнения.
Типы данных:
- Примитивы (String, Integer, Boolean, Date)
- Serializable объекты
- JSON объекты
- File variables для больших данных
// Установка переменных при старте
Map<String, Object> variables = Map.of(
"customerId", "CUST001",
"orderAmount", 1500.0,
"orderDate", new Date(),
"orderItems", Arrays.asList("item1", "item2")
);
ProcessInstance instance = runtimeService.startProcessInstanceByKey(
"orderProcess", variables
);
// Получение переменных
Integer amount = (Integer) runtimeService.getVariable(
instance.getId(), "orderAmount"
);
Variable Scope (Область видимости)
Уровни scope:
- Process Instance - глобальные переменные процесса
- Execution - переменные конкретного execution (в параллельных ветках)
- Task - локальные переменные задачи
// Переменные на уровне execution
execution.setVariableLocal("branchData", "branch1");
// Переменные на уровне task
taskService.setVariableLocal(taskId, "userInput", "approved");
// Получение переменных с учетом scope
Map<String, Object> variables = runtimeService.getVariables(executionId);
Map<String, Object> localVars = runtimeService.getVariablesLocal(executionId);
Task Management
User Tasks (Пользовательские задачи)
Назначение: Задачи, требующие участия человека, управляются через Task Service.
Свойства задачи:
- Assignee - назначенный пользователь
- Candidate Users/Groups - потенциальные исполнители
- Due Date - срок выполнения
- Priority - приоритет
// Получение задач пользователя
List<Task> tasks = taskService.createTaskQuery()
.taskAssignee("john.doe")
.orderByDueDate()
.asc()
.list();
// Задачи группы
List<Task> groupTasks = taskService.createTaskQuery()
.taskCandidateGroup("sales")
.list();
// Назначение задачи
taskService.setAssignee(taskId, "john.doe");
// Завершение задачи
taskService.complete(taskId, Map.of("approved", true));
Task Forms (Формы задач)
Назначение: Определение пользовательского интерфейса для выполнения задач.
Типы форм:
- Generated Forms - автоматически генерируемые из form fields
- External Forms - кастомные HTML формы
- Embedded Forms - встроенные в Camunda Tasklist
// Form Fields в BPMN
<userTask id="approveOrder" name="Approve Order">
<extensionElements>
<camunda:formData>
<camunda:formField id="amount" label="Amount" type="long" />
<camunda:formField id="approved" label="Approved" type="boolean" />
</camunda:formData>
</extensionElements>
</userTask>
// Получение form data
TaskFormData formData = formService.getTaskFormData(taskId);
List<FormField> fields = formData.getFormFields();
External Tasks
External Task Pattern
Назначение: Выполнение задач внешними приложениями через polling механизм. Позволяет интегрировать микросервисы с Camunda.
Преимущества:
- Слабая связанность с Process Engine
- Масштабируемость worker'ов
- Fault tolerance через retry механизмы
// Определение External Task в BPMN
<serviceTask id="processPayment" name="Process Payment">
<extensionElements>
<camunda:properties>
<camunda:property name="type" value="payment" />
</camunda:properties>
</extensionElements>
</serviceTask>
// External Task Worker
@Component
public class PaymentWorker {
@EventListener
public void startWorker(ApplicationReadyEvent event) {
ExternalTaskClient client = ExternalTaskClient.create()
.baseUrl("http://localhost:8080/engine-rest")
.build();
client.subscribe("payment")
.lockDuration(10000)
.handler(this::handlePayment)
.open();
}
private void handlePayment(ExternalTask task, ExternalTaskService service) {
try {
String orderId = task.getVariable("orderId");
// Обработка платежа
processPayment(orderId);
service.complete(task, Map.of("paymentStatus", "SUCCESS"));
} catch (Exception e) {
service.handleFailure(task, e.getMessage(), "Payment failed", 3, 5000);
}
}
}
Retry и Error Handling
Механизмы обработки ошибок:
- Retry - повторная попытка выполнения
- Incident - создание инцидента при исчерпании попыток
- Error Boundary Events - перехват ошибок в BPMN
// Настройка retry в External Task
service.handleFailure(task,
"Payment service unavailable", // errorMessage
"Service temporarily down", // errorDetails
3, // retries
5000 // retryTimeout
);
// BPMN Error Event
throw new BpmnError("PAYMENT_FAILED", "Payment could not be processed");
Process Monitoring
History Service
Назначение: Предоставляет информацию о завершенных процессах, задачах и активностях для анализа и аудита.
Уровни истории:
- NONE - история не сохраняется
- ACTIVITY - сохраняются активности
- VARIABLE - сохраняются изменения переменных
- FULL - полная история включая form properties
// Поиск завершенных процессов
List<HistoricProcessInstance> completedProcesses = historyService
.createHistoricProcessInstanceQuery()
.processDefinitionKey("orderProcess")
.finished()
.finishedAfter(Date.from(Instant.now().minus(30, ChronoUnit.DAYS)))
.orderByProcessInstanceEndTime()
.desc()
.list();
// Анализ времени выполнения
long avgDuration = historyService
.createHistoricProcessInstanceQuery()
.processDefinitionKey("orderProcess")
.finished()
.list()
.stream()
.mapToLong(p -> p.getDurationInMillis())
.sum() / completedProcesses.size();
Metrics и KPIs
Метрики процесса:
- Cycle Time - время от начала до завершения
- Throughput - количество завершенных процессов
- Bottlenecks - узкие места в процессе
// Custom metrics
@Component
public class ProcessMetrics {
@EventListener
public void onProcessEnd(ProcessEndEvent event) {
long duration = event.getProcessInstance().getDurationInMillis();
meterRegistry.timer("process.duration", "processKey",
event.getProcessInstance().getProcessDefinitionKey())
.record(duration, TimeUnit.MILLISECONDS);
}
@EventListener
public void onTaskComplete(TaskCompleteEvent event) {
meterRegistry.counter("tasks.completed", "taskKey",
event.getTask().getTaskDefinitionKey())
.increment();
}
}
Integration Patterns
Message Events
Назначение: Асинхронная коммуникация между процессами или внешними системами через события.
Типы:
- Message Start Event - запуск процесса по сообщению
- Message Intermediate Event - ожидание сообщения в процессе
- Message End Event - отправка сообщения при завершении
// Отправка сообщения в процесс
runtimeService.createMessageCorrelation("OrderApproved")
.setVariable("approvedBy", "manager")
.processInstanceId(processInstanceId)
.correlate();
// Message Listener
@Component
public class OrderMessageListener {
@JmsListener(destination = "order.approved")
public void handleOrderApproved(OrderApprovedMessage message) {
runtimeService.createMessageCorrelation("OrderApproved")
.setVariable("orderId", message.getOrderId())
.correlate();
}
}
Signal Events
Назначение: Broadcast события для уведомления множества процессов одновременно.
// Отправка сигнала всем процессам
runtimeService.signalEventReceived("MarketClosed");
// Отправка сигнала конкретному процессу
runtimeService.signalEventReceived("OrderCancelled", processInstanceId);
REST API Integration
Назначение: Интеграция с внешними REST сервисами через HTTP Connector.
// HTTP Connector в Service Task
<serviceTask id="callExternalService" name="Call External Service">
<extensionElements>
<camunda:connector>
<camunda:connectorId>http-connector</camunda:connectorId>
<camunda:inputOutput>
<camunda:inputParameter name="url">http://api.example.com/orders</camunda:inputParameter>
<camunda:inputParameter name="method">POST</camunda:inputParameter>
<camunda:inputParameter name="headers">
<camunda:map>
<camunda:entry key="Content-Type">application/json</camunda:entry>
</camunda:map>
</camunda:inputParameter>
<camunda:outputParameter name="response">${response}</camunda:outputParameter>
</camunda:inputOutput>
</camunda:connector>
</extensionElements>
</serviceTask>
Performance и Optimization
Database Optimization
Важные аспекты:
- Indexes - на часто запрашиваемые поля
- History Cleanup - регулярная очистка истории
- Batch Operations - для массовых операций
// Batch operations
Batch batch = runtimeService.deleteProcessInstancesAsync(
Arrays.asList("proc1", "proc2", "proc3"),
null, // deleteReason
true, // skipCustomListeners
true // skipSubprocesses
);
// History cleanup
@Scheduled(cron = "0 0 2 * * ?") // Каждый день в 2:00
public void cleanupHistory() {
historyService.cleanUpHistoryAsync(
HistoryCleanupConfiguration.builder()
.batchSize(1000)
.degreeOfParallelism(2)
.build()
);
}
Memory Management
Оптимизации:
- Ограничение размера переменных
- Использование External Tasks для тяжелых операций
- Proper connection pooling
// Оптимизация запросов
List<Task> tasks = taskService.createTaskQuery()
.taskAssignee("user")
.active()
.orderByDueDate()
.asc()
.listPage(0, 50); // Пагинация
// Lazy loading переменных
Map<String, Object> variables = runtimeService.getVariables(
executionId, Arrays.asList("orderId", "amount") // Только нужные переменные
);