Паттерн Saga
В монолитной архитектуре управлять данными достаточно просто. Обычно работает одна база данных, а транзакциями управляют в рамках одной системы.
В архитектуре микросервисов всё иначе. Каждый сервис работает независимо, и у него есть своя база данных. Транзакции, которые затрагивают несколько баз данных, называют распределёнными. Управлять ими сложнее. Они опасны тем, что могут привести к несогласованности данных.
Допустим, вы разрабатываете онлайн-магазин. Логику оплаты товара и логику доставки вынесли в разные микросервисы. Нельзя допустить, чтобы пользователю отправили товар, если оплата заказа не прошла. Или напротив — магазин списал деньги, но не отправил доставку.
Чтобы избежать таких конфликтов и потери данных, в микросервисных приложениях используют паттерн Saga. Его и разберём в этом уроке.
Что такое паттерн Saga
📎 Saga — это архитектурный паттерн для управления распределёнными транзакциями в микросервисных системах. Он обеспечивает целостность данных и позволяет управлять ошибками при выполнении долговременных бизнес-процессов, которые включают несколько микросервисов.
Напомним, что работа с данными должна подчиняться требованиям ACID:
- Atomicity — атомарность,
- Consistency — целостность,
- Isolation — изолированность,
- Durability — надёжность.
В монолитных системах за выполнение требований отвечает реляционная СУБД.
В микросервисных архитектурах тоже нужно поддерживать ACID, но использовать механизмы СУБД не получится: процесс включает несколько баз данных. Saga решает эту проблему с помощью набора локальных транзакций и компенсирующих действий. Проще говоря, паттерн дробит транзакцию на шаги, которыми занимаются разные микросервисы. В случае ошибки система активирует отмену действия, которая распространяется на все вовлечённые микросервисы.
Чтобы увидеть различие этих решений, нужно сравнить реализацию ACID в традиционных системах с реализацией в паттерне Saga.
ACID в монолитных системах
Транзакциями управляют на уровне СУБД с помощью языка SQL и встроенных механизмов транзакционного управления. В случае ошибки автоматически происходит откат транзакции (rollback).
ACID в микросервисах с паттерном Saga
Паттерн можно реализовать двумя способами. Транзакциями управляют через оркестрацию или хореографию. Оркестрация подразумевает наличие отдельного сервиса-оркестратора, а хореография — обмен событиями между микросервисами.
Откат транзакции происходит за счёт вызова компенсирующих действий, которые откатывают изменения, сделанные на предыдущих шагах.
А теперь рассмотрим разные реализации паттерна. Поговорим об оркестрации и хореографии.
Типы реализации Saga
Транзакции в паттерне можно координировать двумя способами:
- через оркестрацию (orchestration),
- через хореографию (choreography).
Оркестрация
В этом случае есть отдельный сервис-оркестратор. Он управляет последовательностью выполнения шагов и при необходимости инициирует компенсирующие действия.
Допустим, компания разрабатывает банковское приложение. Чтобы одобрить пользователю кредит, нужно пройти через несколько проверок и согласований. В процесс вовлечены такие сервисы:
- Orchestrator — управляет процессом.
- Customer Service — проверяет кредитоспособность клиента.
- Anti-fraud Service — проводит проверку на мошенничество.
- Approval Service — принимает или отклоняет заявку на основании результатов предыдущих проверок.
Оркестратор координирует все шаги:
- Начинает с запроса в Customer Service.
- При этом параллельно может отправить запрос в Anti-fraud Service.
- После всех проверок принимает окончательное решение с помощью Approval Service.
Когда использовать оркестрацию
Выбирайте оркестрацию, если у вас:
- Сложные бизнес-процессы. Сложные взаимодействия между микросервисами обычно требуют чёткой последовательности действий. Например, к таким взаимодействиям относятся комплексные финансовые транзакции или процесс подтверждения заказа.
- Потребность в централизованном управлении. Вам нужно централизованно управлять потоком операций и логикой обработки ошибок. Например, у вас есть бизнес-процесс с большим количеством зависимостей между шагами.
- Транзакционное согласование. Требуется координация транзакций с обеспечением согласованности и возможности отката. Например, международные денежные переводы с проверкой валютных курсов и комиссий.
- Сложная бизнес-логика. Нужно, чтобы центральный оркестратор помог в реализации сложной бизнес-логики и управлении состоянием. Например, для бронирования и отмены билетов в авиакомпании.
Хореография
В этом подходе нет центрального управляющего компонента. Каждый микросервис самостоятелен — он знает, что делать после выполнения своего шага. Микросервисы взаимодействуют через события и сами инициируют компенсирующие действия.
Для онлайн-магазина хореографическую сагу можно реализовать так:
- Пользователь создаёт заказ —
NewOrder— через корзину. - Order Service создаёт заказ и отправляет событие
OrderCreated. - Inventory Service получает событие
OrderCreated, резервирует товар и отправляет событиеInventoryReserved. - Payment Service получает событие
InventoryReserved, обрабатывает оплату и отправляет событиеPaymentProcessed. - Shipping Service получает событие
PaymentProcessed, отправляет товар и отправляет событиеOrderShipped.
Когда использовать хореографию
Выбирайте хореографию, если у вас:
- Простые взаимодействия. Взаимодействие между микросервисами относительно простое и не требует сложной координации. Например, это уведомления по электронной почте или сообщения в чате.
- Независимые микросервисы. Микросервисы могут работать независимо друг от друга, а значит, изменение одного сервиса не сильно влияет на другие. Например, логирование событий, аудит и аналитика.
- В приоритете гибкость и масштабируемость. Система должна быть гибкой и легко масштабируемой, а изменения в одном микросервисе не должны требовать изменений в других. Например, вы распространяете маркетинговые предложения через разные каналы.
- Событийно-ориентированная архитектура. Ваша система построена на событиях и естественным образом поддерживает асинхронное взаимодействие. Например, приложение маркетплейса, где события заказа распространяются на службы оплаты, доставки и уведомления.
Итак, есть два способа управления транзакциями в микросервисах — оркестрация и хореография. Ещё в рамках паттерна можно реализовать разные варианты обработки ошибок.
Обработка ошибок и компенсации в Saga
В паттерне Saga обработка ошибок играет ключевую роль. Каждый шаг транзакции выполняют разные микросервисы, поэтому ошибки могут возникнуть на любом этапе. Чтобы обеспечить целостность данных и корректную работу системы, нужно использовать надёжные техники для обработки ошибок.
Шесть самых популярных стратегий:
- компенсационные транзакции,
- повторные попытки — их ещё называют «retries»,
- тайм-ауты и дедлайны,
- Circuit Breaker — эту стратегию переводят как «предохранитель»,
- событийно-ориентированная обработка ошибок,
- системы мониторинга и алертов.
Их можно комбинировать — даже использовать все и сразу. Рассмотрим каждую стратегию по порядку.
1. Компенсационные транзакции
☝ Это основной метод, который используют в Saga для обеспечения целостности данных. К нему добавляют остальные стратегии.
Компенсационные транзакции — это действия, которые откатывают результаты уже выполненных шагов, если на любом из следующих шагов возникла ошибка. Например, заказ зарезервировали и оплатили, но на этапе отправки возникла ошибка. Компенсирующие транзакции отменят и оплату, и резервирование.
Плюсы:
- Отмена изменений обеспечивает целостность данных.
- Можно сохранить результаты успешных операций, если полный откат не нужен.
Минусы:
- Нужно разрабатывать и тестировать компенсирующие транзакции для каждого шага. Это усложняет разработку.
- Пока система выполняет компенсирующие транзакции, данные какое-то время могут оставаться неконсистентными.
2. Повторные попытки
Если транзакция прервалась из-за временной ошибки, система снова попробует выполнить операцию. Попыток будет несколько, и они будут идти друг за другом через одинаковый интервал. Например, микросервис не может подключиться к базе данных. Прежде чем отменить транзакцию, он повторит попытку ещё несколько раз.
Плюсы:
- Увеличивает вероятность успешного завершения транзакции при временных сбоях.
- Просто реализовать для временных ошибок.
Минусы:
- Эта техника не решает проблемы постоянных ошибок.
- Может увеличить нагрузку на систему.
3. Тайм-ауты и дедлайны
Назначают дедлайн — время, за которое операция должна завершиться. Если этого не происходит, возникает ошибка тайм-аута и операцию отменяют. Например, микросервис пытается получить ответ от другого сервиса. Проходит пять секунд, но ответа нет — запрос отменяется.
Плюсы:
- Предотвращает бесконечное ожидание ответов от зависимых сервисов.
- Улучшает отклик системы.
Минусы:
- Сложно определить оптимальное время тайм-аута.
- Эта техника не решает проблему, если ошибка постоянная.
4. Circuit Breaker
Если паттерн повторных попыток заточен под временные проблемы, то паттерн Circuit Breaker — под неожиданные и серьёзные. Он работает как предохранитель: система предотвращает попытки выполнить операцию, если известно, что она не будет успешной. Когда количество ошибок превышает определённый порог, дальнейшие попытки блокируют на некоторое время. Например, микросервис не может подключиться к базе данных. В течение минуты он получил 50 ошибок. Circuit Breaker заблокирует дальнейшие попытки подключения на 30 секунд.
Плюсы:
- Предотвращает перегрузку системы из-за попыток выполнить заведомо неуспешные операции.
- Улучшает устойчивость системы к ошибкам.
Минусы:
- Требует настройки порогов и тайм-аутов.
- Может привести к отказу в обслуживании, если пороги установлены неправильно.
5. Событийно-ориентированная обработка ошибок
Ошибки можно обрабатывать через события. Если в ходе операции в микросервисе возникла ошибка, он опубликует событие о неудаче. Другие микросервисы получат и обработают событие. Например, сервис оформления заказа не может завершить операцию и публикует событие
OrderFailed. В зависимости от типа управления саги, это событие обработает оркестратор или следующий сервис в хореографической цепочке — сервис хранения товаров. Он запустит выполнение компенсирующих действий.Плюсы:
- Обеспечивает асинхронную обработку ошибок.
- Улучшает модульность и расширяемость системы.
Минусы:
- Усложняет отслеживание состояния транзакции и его управление.
- Требует надёжную инфраструктуру для доставки событий.
6. Системы мониторинга и алертов
Чтобы обнаруживать ошибки и быстро на них реагировать, нужно настроить системы мониторинга — например, Prometheus или Grafana. Они будут отслеживать метрики и журналы ошибок. Алерты оповестят команду поддержки, если возникнут критические проблемы.
Плюсы:
- Быстрое обнаружение ошибок и оперативная реакция команды.
- Общая надёжность системы повысится.
Минусы:
- Нужно настроить и поддерживать системы мониторинга и алертов.
- Системы могут ложно срабатывать, если их неправильно настроить.
Итак, вы можете определить, какой тип саги вам нужен, — оркестрация или хореография. А ещё представляете, какие стратегии обработки ошибок можно внедрить для оптимизации ресурсов системы и повышения её надёжности. Осталось разобраться, как внедрить паттерн Saga.
Как внедрить паттерн Saga
Алгоритм состоит из шести шагов. Разберём их по очереди, а чтобы процесс не выглядел абстрактно, покажем некоторые аспекты на примере знакомой компании. Вперёд, «Шайба»!
Шаг 1. Анализ бизнес-процесса и определение шагов Saga
На этом этапе нужно сделать две вещи. Сначала идентифицируйте процесс, а затем определите для каждого шага действия и компенсирующие действия.
Идентифицируйте процесс. Разделите бизнес-процесс на последовательные шаги. Каждый шаг будет выполнять отдельный микросервис.
Определите действия (do) и компенсирующие действия (undo). Для каждого шага определите основное действие и компенсирующее действие, которое нужно выполнить в случае ошибки.
Пример. Для наглядности рассмотрим только процесс создания заказа. В «Шайбе» он состоит из четырёх шагов. За каждый шаг отвечает один сервис:
- Формирование заказа — Order Service.
- Резервирование товара на складе — Inventory Service.
- Оплата заказа — Payment Service.
- Отправка товара клиенту — Shipping Service.
Шаг 2. Проектирование микросервисов
Создайте микросервисы. Каждый из них будет отвечать за выполнение одного шага процесса. У микросервисов должны быть чётко определённые API для выполнения действий. При необходимости можно создать отдельные API для вызова компенсирующих действий.
Обеспечьте идемпотентность операций для всех действий и компенсирующих действий. Для этого используйте уникальные идентификаторы транзакций и проверяйте состояние перед выполнением операции, чтобы избежать повторного выполнения.
Пример. У «Шайбы» получится так.
Order Service:
- Do — создать заказ.
- Undo — отменить заказ.
Inventory Service:
- Do — зарезервировать товар.
- Undo — отменить резервирование.
Payment Service:
- Do — провести оплату.
- Undo — вернуть деньги.
Shipping Service:
- Do — отправить товар.
- Undo — отменить отправку.
Шаг 3. Управление состоянием и взаимодействием микросервисов
Обеспечьте хранение состояния. Каждый микросервис должен хранить состояние своей части транзакции. Например, в собственной базе данных. Состояние должно включать уникальный идентификатор транзакции и текущий статус операции. Статус операции нужен как раз для того, чтобы избежать повторения уже совершённых действий.
Выберите подход к координации шагов Saga. Можно использовать оркестрацию или хореографию. Если выбираете оркестрацию, создайте отдельный сервис-оркестратор, который будет управлять последовательностью выполнения шагов.
Пример. Алекс — архитектор «Шайбы» — решил, что лучше использовать центральный оркестратор. Оформление заказа как раз относится к сложным бизнес-процессам. Оркестратор заказов будет управлять последовательностью выполнения шагов: отслеживать статус каждого шага и инициировать компенсирующие действия при необходимости.
Шаг 4. Реализуйте действия и компенсирующие действия
На этом этапе нужно написать код в каждом сервисе для действий и компенсирующих действий. При этом важно обеспечить идемпотентность каждой операции.
Основные действия (do). Реализуйте основное действие в каждом микросервисе. Используйте уникальные идентификаторы транзакций и проверяйте состояние перед выполнением действия, чтобы избежать повторного выполнения.
Компенсирующие действия (undo). По аналогии с основными действиями напишите компенсирующие.
Шаг 5. Логирование и мониторинг
Логирование. Внедрите детализированное логирование для всех действий и компенсирующих действий. Логи должны включать информацию о транзакциях, их статусе и любых ошибках.
Мониторинг и алерты. Настройте мониторинг микросервисов и оркестратора, если вы его используете. Настройте алерты для оперативного реагирования на ошибки и сбои в выполнении шагов Saga.
Шаг 6. Тестирование
Напишите юнит-тесты для каждого микросервиса, которые будут проверять как основные действия, так и компенсирующие.
Проведите интеграционные тесты, чтобы проверить взаимодействие микросервисов в рамках Saga. Тесты должны включать сценарии с успешным выполнением всех шагов и сценарии с ошибками, которые проверят вызов компенсирующий действий.
Ещё проведите нагрузочное тестирование, чтобы оценить производительность и устойчивость системы при высоких нагрузках.
На этом всё — ваша сага готова.
Перечислим инструменты и фреймворки, которые поддерживают реализацию Saga.
Инструменты и фреймворки
- Long Running Actions for MicroProfile — спецификация представляет API для работы служб по координации активности.
- Axon — это платформа для создания приложений, основанных на трёх концепциях (CQRS, Event Sourcing и DDD).
- Eventuate Tram Sagas — это фреймворк оркестровки Saga для микросервисов на Java, которые используют JDBC или JPA.
- Oracle Database — можно использовать как платформу для создания приложений на базе Saga, в облаке или в локальной сети.
- Seate Saga Mode — это решение для длинных транзакций от компании SEATA.
- Apache Camel Saga — Saga EIP предоставляет возможность определить серию связанных действий в маршруте Camel, которые должны быть либо успешно завершены, либо компенсированы.
Итоги
Успех микросервисной архитектуры во многом зависит от того, насколько эффективно вы сможете управлять данными и транзакциями. В архитектуре микросервисов транзакции носят распределённый характер, потому что в них участвуют разные базы данных.
Паттерн Saga — это мощный инструмент для управления распределёнными транзакциями в архитектуре микросервисов.
В этом уроке вы изучили основные концепции этого паттерна: транзакции делят на шаги и применяют компенсирующие действия в случае ошибок. Реализовать Saga можно через оркестрацию или хореографию. В любом случае важно обеспечить идемпотентность операций.
Ещё обсудили различные техники обработки ошибок: компенсирующие транзакции, повторные попытки, тайм-ауты, паттерн Circuit Breaker, событийно-ориентированную обработку ошибок и системы мониторинга и алертов.
Что ещё почитать про Saga
Это последний урок темы. Дальше — проектная работа.