Ответ
Saga — это паттерн управления распределёнными транзакциями, который помогает поддерживать консистентность данных в микросервисной архитектуре без использования двухфазных коммитов (2PC).
Основная идея — разбить одну большую транзакцию на последовательность локальных транзакций, выполняемых в каждом сервисе. Если какая-либо локальная транзакция завершается неудачей, Saga запускает компенсирующие транзакции, чтобы отменить предыдущие успешные шаги.
Существует два основных подхода к координации саги:
1. Хореография (Choreography)
В этом подходе нет центрального координатора. Каждый сервис после выполнения своей транзакции публикует событие в общую шину (например, Kafka или RabbitMQ). Другие сервисы подписываются на эти события и реагируют на них, запуская свои локальные транзакции.
- Плюсы: Децентрализация (нет единой точки отказа), слабая связанность сервисов.
- Минусы: Сложно отслеживать и отлаживать весь процесс, так как логика распределена по многим сервисам. Возможен риск циклических зависимостей между событиями.
Пример логики:
Сервис Заказов создает заказ и публикует событие OrderCreated
. Сервис Оплаты слушает это событие, списывает средства и публикует PaymentCompleted
.
2. Оркестрация (Orchestration)
В этом подходе существует центральный сервис-координатор (оркестратор), который управляет всем процессом. Он отправляет команды сервисам-участникам и ожидает от них ответа. Оркестратор знает всю последовательность шагов и отвечает за запуск компенсирующих транзакций в случае сбоя.
- Плюсы: Централизованная логика, которую легко понять, отладить и изменить. Проще управлять процессом компенсации.
- Минусы: Оркестратор становится единой точкой отказа и потенциальным узким местом. Сервисы становятся более связанными с оркестратором.
Пример логики:
// Псевдокод оркестратора
func ExecuteOrderSaga(orderID string) error {
if err := orderService.Create(orderID); err != nil {
return err // Начальный шаг не удался
}
if err := paymentService.Process(orderID); err != nil {
// Запуск компенсирующей транзакции
orderService.Cancel(orderID)
return err
}
if err := deliveryService.Ship(orderID); err != nil {
// Запуск двух компенсирующих транзакций
paymentService.Refund(orderID)
orderService.Cancel(orderID)
return err
}
return nil
}
Критерий | Хореография | Оркестрация |
---|---|---|
Координация | Децентрализованная (через события) | Централизованная (сервис-оркестратор) |
Связанность | Низкая | Высокая (с оркестратором) |
Отладка | Сложная | Относительно простая |
Точка отказа | Нет единой | Оркестратор |