Введенный в 2015 году, паттерн внешней задачи (external task pattern) становится всё более популярным. Вместо того чтобы движок процессов активно вызывал какой-либо код (push), паттерн внешней задачи добавляет работу в некую очередь и позволяет воркерам (workers) извлекать их по мере необходимости. Этот метод также известен как публикация/подписка (publish/subscribe). Движок процессов публикует задачи, а воркеры подписываются на их выполнение.
В рамках Camunda Platform 7 мы работаем над тем, чтобы паттерн внешних задач стал стандартной рекомендацией, а для Camunda Cloud это вообще единственный способ написания связующего кода. В частности, использование Java-делегатов больше не поддерживается в Camunda Cloud. Это иногда ставит людей в тупик, поэтому этот пост ответит на вопрос о том, почему это не является проблемой, и подробно рассмотрит преимущества, которые вы можете получить от внешних задач. Этот пост также развенчает некоторые мифы об этом паттерне:
Вы все равно можете вызывать конечные точки ваших сервисов через любой протокол (например, REST, AMQP, Kafka).
Весь код воркеров может находиться в одном Java-приложении.
Модель программирования оказывается удивительно похожей на Java-делегаты при использовании Spring.
Обработка исключений также может быть доверена BPM-движку.
Накладные расходы на производительность невелики.
Архитектурные соображения
Давайте развенчаем два архитектурных мифа, связанных с внешними задачами.
Во-первых, использование внешних задач не означает, что сервисы, которые вы раньше вызывали через REST, теперь должны самостоятельно извлекать свои задачи. Хотя это является архитектурным вариантом, это не типичный случай. Рассмотрим пример:
Вы, вероятно, реализуете воркера, который всё равно будет выполнять REST-вызов к микросервису обработки платежей (левая сторона иллюстрации выше). Это API, которое микросервис предоставляет, и его следует использовать. Воркеры находятся в рамках решения по обработке заказов или микросервиса. Никто за пределами команды по обработке заказов не должен даже знать, что используется Camunda или воркер внешней задачи.
Сравните это с решением на правой стороне примера, где микросервис обработки платежей напрямую извлекает свои задачи из Camunda. В этом случае Camunda выступает в качестве посредника (middleware), используемого для взаимодействия различных микросервисов. Хотя это возможно и имеет свои преимущества, я редко встречал такие решения на практике.
Второй миф заключается в том, что вам нужно писать несколько приложений, если у вас есть несколько сервисных задач, по одному для каждого воркера внешней задачи. Хотя вы можете разделить воркеров на несколько приложений, это встречается редко. Гораздо более распространено запускать всех (или, по крайней мере, большинство) ваших воркеров в одном приложении.
Это приложение логически связано с решением по автоматизации процессов и регистрирует воркера для каждой внешней задачи. Оно также способно автоматически развертывать модель процесса.
Написание связующего кода (glue code)
Это подводит нас к вопросу о том, как писать связующий код. В этой области существует ещё один миф: это должно быть сложно, поскольку задействована удалённая связь. Хорошая новость заключается в том, что это не обязательно верно для Camunda Cloud, так как существуют клиентские библиотеки для различных языков программирования, которые обеспечивают отличное взаимодействие для разработчиков.
Например, используя интеграцию с Spring, вы можете написать код воркера следующим образом:
public class RetrieveMoneyWorker {
@JobWorker(type = "retrieveMoney", autoComplete = false)
public void retrieveMoney(final JobClient client, final ActivatedJob job) {
// Your logic here
client.newCompleteCommand(job.getKey()).send();
}
}
public class FetchGoodsWorker {
@JobWorker(type = "fetchGoods", autoComplete = false)
public void fetchGoods(final JobClient client, final ActivatedJob job) {
// Your logic here
client.newCompleteCommand(job.getKey()).send();
}
}
Если сравнить этот код с Java-делегатом, он выглядит удивительно похоже. Мы даже создали расширение для сообщества, содержащее адаптер для повторного использования существующих Java-делегатов в Camunda Cloud. Хотя я не стал бы настоятельно рекомендовать это, так как лучше вручную мигрировать ваши классы, это хорошее подтверждение того, что концептуально это не так уж и сложно.
Таким образом, стоит отметить, что есть некоторые вещи, которые вы могли делать в Java-делегатах, но которые больше недоступны в воркерах внешних задач:
Доступ к внутренним механизмам движка процессов
Влияние на поведение движка процессов
Интеграция с пулом потоков или транзакциями движка процессов
Использование «грязных» трюков с помощью ThreadLocal или подобных подходов
В общем, я считаю, что это хорошо, что вы больше не можете выполнять эти действия, так как они регулярно приводили к проблемам.
Обработка исключений
При написании связующего кода вы также можете передавать проблемы внутри вашего воркера в движок процессов для их обработки. Например, движок процессов может инициировать повторную попытку выполнения или создание инцидента в операционных инструментах. Код довольно простой и, как и прежде, вполне сопоставим с Java-делегатами:
Read more on this in the spring-zeebe docs.
Тем не менее, существует один важный случай сбоя, который пока не решён должным образом: что делать, если воркер выходит из строя и больше не извлекает задачи? В настоящее время Camunda Cloud распознаёт это косвенно, когда сервисные задачи не обрабатываются слишком долго. Идеально было бы, чтобы сам движок процессов мог обнаружить, что задачи больше не извлекаются. В этом случае он мог бы указать на это в операционных инструментах. В настоящее время мы изучаем эту функцию. Пока вы можете полагаться на типичный системный мониторинг для обнаружения сбоя Java-приложения воркера.
А что насчёт транзакций?
Аналогично, как вы достигаете согласованности между вашим бизнес-кодом и движком процессов? Что делать, если какой-либо из компонентов выходит из строя? При использовании Java-делегатов многие пользователи передавали эти проблемы менеджеру транзакций, часто не осознавая этого. Пожалуйста, ознакомьтесь с постом о том, как добиться согласованности без менеджеров транзакций, чтобы узнать, как справляться с этим с внешними задачами, а также понять, почему это предпочтительная концепция на сегодняшний день.
Задержки при удалённой коммуникации
Последний миф, который я хотел бы обсудить в этом посте, заключается в том, что удалённые воркеры якобы должны быть «медленными». Часто в таких обсуждениях термин «медленный» не определяется, но если взглянуть на Camunda Platform 7, то в зависимости от конфигурации исполнителя задач действительно может потребоваться несколько секунд для того, чтобы внешняя задача была взята на выполнение (хотя это можно оптимизировать!) В Camunda Cloud вся взаимодействие оптимизировано изначально, так что добавляется лишь небольшая задержка из-за удалённой коммуникации. В недавнем эксперименте я измерил накладные расходы удалённого воркера и получил примерно 50 мс.
В большинстве проектов это вовсе не является проблемой, особенно поскольку это не влияет на пропускную способность движка процессов. Другими словами, вы по-прежнему можете обрабатывать то же количество экземпляров процессов, просто каждое выполнение сервисной задачи будет занимать на 50 мс дольше. Обратите внимание, что мы продолжаем оптимизировать это время для сценариев с низкой задержкой, которые востребованы среди наших клиентов.
Заключение
Как вы могли заметить, вы получаете модель программирования, которая столь же удобна, как и Java-делегаты. В то же время ваш код правильно изолирован от движка процессов (переход от встроенных к удалённым BPM-движкам открывает все преимущества удалённой конфигурации).
Вот почему я лично так рад переходу на внешние задачи в качестве стандартной модели связующего кода в Camunda.
Подписывайтесь на Telegram канал BPM Developers. Рассказываем про бизнес процессы: новости, гайды, полезная информация и юмор.
5 февраля в 14:00 мск пройдет презентация новой open source платформы OpenBPM. Регистрируйтесь и приходите.
SkillMax999
Интересная статья.
Стоит попробовать на практике. Я в своё время много намучился с Камундой, т.к. нормальных гайдов и примеров сложно было найти.