Как стать автором
Обновить

Проектируем архитектуру Camunda Cloud: подключаем движок процессов к вашему миру

Время на прочтение8 мин
Количество просмотров498
Автор оригинала: Бернд Рюккер (Bernd Ruecker) — соучредитель и главный технолог Camunda

Вы начали свой первый проект, используя автоматизацию бизнес-процессов как сервис с Camunda Cloud? Одной из первых задач будет набросать базовую архитектуру вашего решения. Этот блог-пост поможет вам ответить на важные начальные вопросы: как подключить движок выполнения процессов Zeebe к вашему приложению или к внешним системам? Что такое job worker, какую роль он играет и сколько их вообще нужно?

Подключение движка процессов к вашему приложению

Движок процессов Zeebe — это удалённая система для вашего приложения, аналогично базе данных. Приложение подключается к Zeebe по протоколу — gRPC, который, как правило, скрыт от вас (аналогично драйверам ODBC или JDBC).

С Camunda Cloud и движком Zeebe есть два основных способа подключения:

  1. Вы пишете программный код с использованием клиентской библиотеки под нужный язык.

  2. Используете готовый коннектор или «мост» (о терминологии позже) — требуется лишь конфигурация.

Как и всегда, у обоих подходов есть свои плюсы и минусы. Разберём их подробнее.

Связующий код (glue code)

Чтобы подключиться к Zeebe, вы внедряете клиентскую библиотеку Zeebe в своё приложение (или микросервис). Если у вас несколько приложений, каждому понадобится библиотека. Если библиотека под ваш язык отсутствует, вы можете сгенерировать gRPC-клиента самостоятельно.

С помощью клиента ваше приложение может:

  1. Вызывать Zeebe — запускать процессы, отправлять сообщения, деплоить схемы.

  2. Подписываться на задачи, созданные движком в контексте BPMN service tasks.

Вызов Zeebe

С помощью API клиента Zeebe вы можете взаимодействовать с движком процессов. Два наиболее важных вызова API — это запуск новых экземпляров процессов и корреляция сообщений с экземпляром процесса.

Запуск экземпляров процессов с использованием Java-клиента:

processInstance = zeebeClient.newCreateInstanceCommand()
  .bpmnProcessId("someProcess").latestVersion()
  .variables( someProcessVariablesAsMap )
  .send()
  .exceptionally( throwable -> { throw new RuntimeException("Could not create new instance", throwable); });

Запуск экземпляров процессов с использованием NodeJS-клиента:

const processInstance = await zbc.createWorkflowInstance({
  bpmnProcessId: 'someProcess', 
  version: 5,
  variables: {
    testData: 'something',
  }
})

Корреляция сообщений с экземплярами процессов с использованием Java-клиента:

zeebeClient.newPublishMessageCommand()
  .messageName("messageA")
  .messageId(uniqueMessageIdForDeduplication)
  .correlationKey(message.getCorrelationid())
  .variables(singletonMap("paymentInfo", "YeahWeCouldAddSomething"))
  .send()
  .exceptionally( throwable -> { throw new RuntimeException("Could not publish message " + message, throwable); });

Корреляция сообщений с экземплярами процессов с использованием NodeJS-клиента:

zbc.publishMessage({
  name: 'messageA',
  messageId: messageId,
  correlationKey: correlationId,
  variables: { 
    valueToAddToWorkflowVariables: 'here', 
    status: 'PROCESSED' 
  },
  timeToLive: Duration.seconds.of(10)
})

Это позволяет вам подключить Zeebe к любой внешней системе, написав немного собственного glue-кода. Мы рассмотрим распространённые примеры технологий, чтобы проиллюстрировать это далее.

Подписка на задачи с использованием job worker

Чтобы реализовать сервисные задачи модели процесса, вы можете написать код, который подписывается на движок процессов. По сути, вы напишете glue-код, который будет вызываться каждый раз, когда достигается сервисная задача (которая внутренне создаёт задание, отсюда и название).

Glue-код на Java:

class ExampleJobHandler implements JobHandler {
  public void handle(final JobClient client, final ActivatedJob job) {
    // here: business logic that is executed with every job
    client.newCompleteCommand(job.getKey()).send()
      .exceptionally( throwable -> { throw new RuntimeException("Could not complete job " + job, throwable); });;
  }
}

Glue-код на NodeJS:

function handler(job, complete, worker) {
  // here: business logic that is executed with every job
  complete.success()
}

Теперь этот обработчик нужно подключить к Zeebe, что обычно делается через подписки, которые опрашивают сервер для получения заданий (long polling).

Открытие подписки с использованием Zeebe Java-клиента:

zeebeClient
  .newWorker()
  .jobType("serviceA")
  .handler(new ExampleJobHandler())
  .timeout(Duration.ofSeconds(10))
  .open()) {waitUntilSystemInput("exit");}

Открытие подписки с использованием Zeebe NodeJS-клиента:

zbc.createWorker({
  taskType: 'serviceA',
  taskHandler: handler,
})

Вы также можете использовать интеграции в некоторых программных фреймворках, таких как Spring Zeebe в мире Java, которая запускает job worker и реализует подписку автоматически в фоне для вашего glue-кода.

Подписка для glue-кода автоматически открывается через интеграцию Spring:

@ZeebeWorker(type = "serviceA")
public void handleJobFoo(final JobClient client, final ActivatedJob job) {
  // here: business logic that is executed with every job
  client.newCompleteCommand(job.getKey()).send()
    .exceptionally( throwable -> { throw new RuntimeException("Could not complete job " + job, throwable); });;
}

Примеры технологий

Большинство проектов хотят подключаться к определённым технологиям, и сейчас чаще всего спрашивают про REST, обмен сообщениями или Kafka. Давайте посмотрим, как можно работать с этими технологиями.

REST

Вы можете написать фрагмент кода, который предоставляет REST endpoint на выбранном языке, и затем запускает экземпляр процесса.

Демо-проект Ticket Booking содержит пример с использованием Java и Spring Boot для REST endpoint. Аналогично, вы можете использовать расширение Spring Boot для запуска job worker’ов, которые выполняют исходящие REST-вызовы.

Вы можете найти пример кода на NodeJS для REST endpoint в примере Flowing Retail.

Обмен сообщениями

Вы можете сделать то же самое для сообщений, что сейчас часто означает использование AMQP.

Пример Ticket Booking содержит пример для RabbitMQ, Java и Spring Boot. В нем есть listener для сообщений, чтобы коррелировать входящие сообщения с ожидающими экземплярами процессов, и glue-код для отправки исходящих сообщений в брокер сообщений.

Обратите внимание, что поддержка элемента send task, используемого в этой диаграмме, будет добавлена в Camunda Cloud 1.1 в июле 2021 года. До версии 1.1 вам, возможно, придётся использовать service task в качестве обходного пути.

В следующем посте этой серии вы узнаете, почему я использовал send и receive task здесь, а не просто service task, который технически тоже бы сработал (спойлер: потому что сервис оплаты может выполняться долго — например, если нужно обновить истёкшие кредитные карты или выполнить банковский перевод).

Та же концепция применима и к другим языкам программирования — например, вы можете использовать клиент NodeJS для RabbitMQ и клиент NodeJS для Zeebe, чтобы создать тот же тип glue-кода, как показано выше.

Apache Kafka

Вы можете использовать тот же подход с Kafka-топиками. Пример Flowing Retail показывает это с использованием Java, Spring Boot и Spring Cloud Streams. В нём есть код для подписки на Kafka-топик и запуска новых экземпляров процессов при получении новых записей, а также glue-код для создания новых записей при выполнении service task. Конечно, вы можете использовать и другие фреймворки для достижения того же результата.

Построение приложений с glue-кодом

Обычные приложения включают несколько участков glue-кода в одной кодовой базе.

Например, микросервис онбординга, показанный на рисунке выше, включает:

  • REST endpoint, запускающий экземпляр процесса (1)

  • саму схему процесса (2), вероятно, автоматически развёрнутую в движке процессов при старте приложения

  • glue-код, подписывающийся на две service tasks, которые вызывают удалённый REST API (3) и (4)

Job worker будет автоматически запущен как часть приложения для обработки подписок. В этом примере приложение написано на Java, но, опять же, это может быть любой поддерживаемый язык программирования.

Использование готовых коннекторов или мостов

Как вы уже увидели, glue-код довольно прост, но вам приходится писать код. Иногда может быть предпочтительнее использовать готовый компонент, который подключает Zeebe к нужной технологии просто за счёт конфигурации. Такой компонент называется «connector» или «bridge» (я поясню терминологию ниже, оба термина можно считать синонимами).

Коннектор может быть одно- или двусторонним и обычно представляет собой отдельное приложение, реализующее соединение и трансляцию в одном или обоих направлениях. Такой коннектор может быть полезен и в случаях, когда интеграция становится сложной.

Например, HTTP-коннектор — это односторонний коннектор, содержащий job worker, который может обрабатывать сервисные задачи, выполняющие HTTP-запросы, как показано на следующей иллюстрации.

Другой пример — Kafka-коннектор, как показано ниже.

Это двусторонний коннектор, который содержит Kafka-listener для пересылки Kafka-записей в Zeebe, а также job worker, создающий Kafka-записи каждый раз, когда выполняется service task. Это показано на следующем примере.

Использование коннекторов в Camunda Cloud

На данный момент коннекторы не управляются как часть Camunda Cloud, что означает, что вам нужно управлять ими самостоятельно в вашей среде — будь то частное или публичное облако.

Плюсы и минусы коннекторов

В общем, коннекторы — интересный способ решения сложных интеграций, где не требуется кастомизация. Например, мост Camunda RPA для подключения RPA-ботов (вскоре будет доступен для Camunda Cloud).

Коннекторы хорошо подходят для сценариев, где не нужен собственный glue-код, например, при оркестрации serverless-функций AWS с AWS Lambda Connector. Такой коннектор можно один раз запустить и использовать в разных процессах.

Но у коннекторов есть и общие недостатки:

  1. Возможности ограничены тем, что предусмотрел автор коннектора. На практике у вас могут быть немного другие требования, и вы вскоре столкнётесь с ограничениями коннектора.

  2. Коннектор требует, чтобы вы управляли им в дополнение к вашему собственному приложению. Сложность, связанная с этим, зависит от вашей среды.

  3. Тестировать ваш glue-код становится труднее, так как вы не можете легко подключить mock-объекты в такой коннектор — в отличие от собственного glue-кода.

Общее эмпирическое правило: предпочитайте собственный glue-код, если у вас нет веской причины использовать существующий коннектор (как описано выше).

Переиспользование собственной интеграционной логики через выделение коннекторов

Если вам нужно регулярно интегрироваться с определённой инфраструктурой, например, с вашей CRM-системой, вы можете захотеть создать собственный CRM-коннектор, централизованно его развернуть и использовать в разных приложениях.

В целом мы не рекомендуем начинать с таких коннекторов слишком рано. Не забывайте, что такой коннектор становится трудно настраиваемым после запуска в продакшене и при использовании в нескольких приложениях. Также часто гораздо сложнее правильно извлечь все параметры конфигурации и передать их из процесса, чем просто написать собственный glue-код на нужном языке программирования.

Так что стоит выделять полноценный коннектор только в том случае, если вы точно понимаете, что вам нужно.

Не забывайте и о возможности выделения общего glue-кода в простую библиотеку, которую можно использовать в разных местах. Но учтите, что обновление библиотеки, используемой в различных других приложениях, может быть сложнее, чем обновление одного централизованного коннектора. Как и во всём в жизни, лучший подход зависит от вашей ситуации.

Но если у вас уже есть такой glue-код в работе, и вы действительно понимаете последствия превращения его в коннектор, а также ту ценность, которую это принесёт — это вполне может иметь смысл.

Глоссарий

Я не хочу завершать этот пост, не подведя итоги по используемой терминологии (вам также может быть интересно заглянуть в глоссарий Camunda Cloud):

  • Bridge: синоним «connector».

  • Connector: программный компонент, который соединяет Zeebe с другой системой или инфраструктурой. Может быть одно- или двусторонним и возможно включает job worker. Граница между connector и job worker может быть размыта — в целом, коннекторы подключаются к другим активным программным компонентам. Например, «DMN connector» может подключать Zeebe к управляемому DMN‑движку, а «DMN worker» будет использовать DMN-библиотеку для выполнения решений.

  • Glue Code: любой программный код, связанный с моделью процесса (например, обработчик job worker’а).

  • Job Worker: активный программный компонент, который подписывается на Zeebe для выполнения доступных заданий (обычно когда экземпляр процесса достигает service task).

  • Worker: синоним «job worker».

Заключение

Этот блог-пост провёл вас по основам подключения Zeebe — движка процессов в составе Camunda Cloud — к вашему окружению. В основном это касается написания собственного glue-кода на предпочитаемом вами языке программирования и использования существующих клиентских библиотек. В некоторых случаях вы также можете захотеть использовать готовые коннекторы или мосты — по крайней мере, как отправную точку.

Подписывайтесь на наш телеграм-канал BPM Developers — про бизнес-процессы: новости, гайды, полезная информация и юмор.
Подписывайтесь на наш телеграм-канал BPM Developers — про бизнес-процессы: новости, гайды, полезная информация и юмор.
Теги:
Хабы:
+5
Комментарии0

Публикации

Работа

Ближайшие события