Camunda и распределённые транзакции на примере
Распределенные транзакции в микросервисах могут принести много боли разработчикам. Как за 30 минут сделать распределенные транзакции на Camunda BPM и перестать страдать — читайте в этой статье. Ссылки на github и видео-версия внутри. Статья для программистов, системных аналитиков и архитекторов. ¯ \ _ (ツ) _ / ¯.
Видеоверсия
Что за распределённые транзакции и способы решения
Бывает такое, что разработчикам нужно совершить несколько операций с разными сущностями в рамках одной логической операции. Допустим, клиент сделал заказ и мы должны списать с него деньги за место в самолете. Но сделать это можно только после бронирования самолета. Т.е. разработчика интересует либо списание+бронирование, либо ничего.
В монолитных системах, когда и за деньги, и за билеты, отвечает одно приложение и одна база данных, такое поведение достигается по-умолчанию за счёт транзакций в базе.
В сервисной архитектуре, когда за деньги отвечает платежный шлюз, а за билеты система бронирования, возникают распределённые транзакции.
1 Способ решения: 2 phase-commit
Этот вариант заключается в достижении консенсуса сервисами: управляющий транзакциями сервис информирует сервисы и необходимости подготовиться к транзакции, и если все готовы — командует её выполнить.
2 Способ решения: Saga
В этом варианте разработчик работает с заданиями, которые можно откатить. Т.е. сначала выполняются все действия, после проверяется что всё ок, и транзакция закрывается. Если произошла ошибка, то выполненные действия откатываются.
Как правило, 2PC используется для мгновенных транзакций: например в одной сети, а Saga — для длительных: например с участием внешних систем.
Я предпочитаю Saga, особенно в реализации с Camunda, потому что их удобнее модифицировать и они более универсальные.
Пример: перевод средств с одного сервиса в другой в Camunda
Для реализации распределённой транзакции мы воспользуемся BPM движком Camunda. Вот такую схему мы реализуем:
- Jmeter используем для запуска 9000 инстансов
- Camunda для реализации Saga, вот репозиторий проекта. Проект собран на базе вот этого поста
- Microservice 1, Microservice 2 — простое приложение на Kotlin и Javalin.io , которое хранит в оперативной памяти состояние аккаунта. Вот репозиторий.
- Excamad — дополнительная админка к Camunda, которая позволяет смотреть историю процессов. Вот репозиторий.
Распределенные транзакции (SAGA) в BPMN
В BPMN SAGA реализуется с помощью символа компенации и встроенного транзакционного подпроцесса:
Транзакционный процесс гарантирует, что либо процесс будет выполнен успешно, т.е. дойдет до завершающего события, либо (при срабатывании Cancel Event), будут выполнены все компенсации.
На диаграмме в голубом квадратике 40% транзакций определяются в ошибку (через случайное число), что приводит процесс к Cancel event и запуску компенсаций «Вернуть на 1», и «Списать с 2».
Таким образом мы получаем согласованность в конечном итоге сумме в двух микросервисах.
Запуск и результаты
В итоге, запустив 9000 процессов, я получил такие результаты:
Как вы видите, сумма не равна 9000*50(это размер одного платежа) =450 000, т.е. были ошибочные транзакции. Которые отработали вот так:
В итоге
Сделать Saga на Camunda довольно легко. У вас есть задачи распределённых транзакций? Как вы их решаете?
Комментарии