В микросервисных продуктах гораздо сложнее, чем в
монолитах, выстраивать последовательные цепочки операций.
Распределённые транзакции создают угрозу несогласованности данных,
и разработчикам приходится придумывать, как научить микросервисы
отчитываться о своих действиях. Сегодня расскажем про шаблон
проектирования Saga, который мы используем, чтобы управлять
транзакциями в наших продуктах.
Монолитное приложение работает с одной базой данных, у каждой
транзакции есть начало и конец. Мы можем быть уверены, что данные
любого многоступенчатого процесса в такой архитектуре будут
согласованы.
Когда мы переходим на микросервисы, эта уверенность пропадает.
Каждый микросервис работает сам по себе, записывает свои данные в
собственную базу, да и базы эти могут быть принципиально разными.
Операции носят асинхронный характер, и чётко проконтролировать их
работу в рамках многоступенчатого процесса практически
невозможно.
Пара примеров, чем грозит несогласованность
данных
Возьмём для примера банковское приложение с возможностью перевода
денег. Эта функция работает на двух микросервисах: один отвечает за
снятие денег с баланса, второй за их зачисление на целевой счёт.
Если разработчики не позаботились о согласованности данных, сбой
одного из микросервисов или шины, через которую они общаются, может
привести к потере денег.
Другой пример представим большой корпоративный продукт со сложной
внутренней логикой. Скажем, система электронного документооборота
она принимает сканы от пользователя, затем одни данные отправляет в
бухучёт, другие в финансовые системы, сами документы складывает в
электронный архив и т.д. Без контроля распределённых транзакций все
внутренние процессы становятся чёрным ящиком. Если возникают
ошибки, мы не можем остановить и перезапустить процесс. Не можем
загрузить часть правильно оформленных документов и вернуть
остальные пользователю, работа идёт по принципу всё или ничего.
Чтобы в микросервисном продукте не происходили такие конфликты,
данные не терялись и были согласованными, мы используем шаблон
проектирования Saga.
Как работает Saga
Технически Saga это отдельный микросервис, который через шину ловит
сообщения о событиях в других микросервисах. Есть два варианта
работы Saga:
-
Saga сверяется с шаблоном процесса и отдаёт команду другим модулям.
-
Другие модули сами подписываются на события, которые влияют на их активность и при наступлении события действуют по шаблону процесса.
Такой подход применяется как в краткосрочных процессах, так и в
длительных, которые могут требовать множества последовательных
действий, перехода из системы в систему, и продолжаться несколько
дней.
Применение Saga в нашей практике
Пример 1. В системе для работы с медиафайлами
класса DAM (Digital Asset Management), пользователи за раз могут
загружать десятки фото- и видеоматериалов, каждый из которых при
добавлении проходит через несколько микросервисов.
Без Saga пользователям непонятно, когда эти файлы становятся
доступны для дальнейшей обработки, а если страница зависает или
обновляется, процесс загрузки приходится начинать заново.
Saga позволяет нам показывать пользователям прогресс загрузки,
запрашивать у них дополнительные данные, не прерывая процесс, а
также отменять загрузку например, если система видит, что
пользователь приложил неправильный файл.
Пример 2. В системе авансовых отчетов по
командировкам сотрудники последовательно указывают различные
данные, загружают чеки и документы. Saga координирует весь этот
процесс в полном соответствии с внутренними регламентами компании.
Архитектура продукта выстроена по образцу бизнес-процесса, который
приходилось бы заполнять в офлайне. В результате ИТ-система
работает прозрачно для бизнеса.
Альтернативные подходы, или почему мы выбрали
Saga
Координатор распределённых транзакций. Принцип
похож на Saga, только вместо того, чтобы слушать шину данных и
отправлять через неё сообщения микросервисам, оркестратор работает
напрямую с БД микросервисов.
Ограничения:
-
Этот вариант технически более сложен.
-
А если у микросервисов разный тип БД (например, MS SQL и MongoDB), этот метод в принципе не сработает.
Outbox-Inbox. Микросервисы обмениваются
сообщениями напрямую, без оркестратора. Если вернуться к примеру с
банковским приложением, то при переводе денег один микросервис
сохраняет у себя в Outbox сообщение об отправке, второй записывает
у себя в Inbox сообщение о получении. Если случается сбой, то можно
соотнести между собой их ящики и добиться согласованности
данных.
Ограничения:
-
Это вполне работоспособный метод, но подходит только для простых процессов если в транзакции участвует три и более сервисов, контролировать их взаимодействие становится слишком затратным.
Здесь и стоит задуматься о Saga.