Как управлять распределенной транзакцией между несколькими микросервисами?

Ответ

В микросервисной архитектуре классические двухфазные коммиты (2PC) не подходят из-за требований к доступности и слабой связанности. Основной подход — Паттерн Saga.

Суть: Длинная бизнес-транзакция разбивается на последовательность локальных транзакций в каждом сервисе. Для каждого шага определяется компенсирующее действие (Compensating Transaction), которое откатывает изменения в случае сбоя.

Два стиля координации:

  1. Оркестрация (Orchestration):

    • Центральный координатор (оркестратор) управляет потоком, вызывает сервисы и решает, что делать при ошибке.
    • Пример на C#:
      public class OrderOrchestrator
      {
      public async Task ProcessOrder(Order order)
      {
          try
          {
              await _paymentService.ChargeAsync(order); // Шаг 1
              await _inventoryService.ReserveAsync(order); // Шаг 2
              await _notificationService.SendConfirmationAsync(order); // Шаг 3
          }
          catch (Exception)
          {
              // Компенсация (обратный порядок)
              await _inventoryService.ReleaseAsync(order);
              await _paymentService.RefundAsync(order);
              throw;
          }
      }
      }
  2. Хореография (Choreography):

    • Сервисы общаются через события (через брокер: Kafka, RabbitMQ). Каждый сервис слушает события и выполняет свой шаг, публикуя следующее событие.
    • Пример потока событий: OrderPlaced -> PaymentProcessed -> InventoryReserved. Если сервис инвентаря не может выполнить шаг, он публикует событие InventoryReservationFailed, которое запускает компенсирующие события (PaymentRefunded).

Ключевые компромиссы:

  • Оркестрация проще для контроля и мониторинга, но создает точку централизации.
  • Хореография более распределенная и гибкая, но сложнее в отладке и отслеживании общего состояния процесса.

Инструменты: Для сложных сценариев можно использовать специализированные фреймворки (например, на основе состояния, сохраняемого в БД) или платформы вроде Azure Durable Functions для оркестрации.

Ответ 18+ 🔞

Слушай, а вот эти твои классические двухфазные коммиты в микросервисах — это как пытаться завести "Запорожец" в космосе. В теории, может, и взлетит, но на практике — нихуя. Требования к доступности и слабой связанности их просто нахуй посылают. Так что основной подход тут — Паттерн Saga, без вариантов.

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

А координировать это безобразие можно двумя способами:

  1. Оркестрация (Orchestration):

    • Тут есть главный по тарелочкам — центральный координатор, или оркестратор. Он, как мама в семье, всем командует: вызывает сервисы, ругается, если что-то сломалось, и решает, как откатываться.
    • Вот тебе пример на C#, чтоб было понятнее:
      public class OrderOrchestrator
      {
      public async Task ProcessOrder(Order order)
      {
          try
          {
              await _paymentService.ChargeAsync(order); // Шаг 1: списать бабки
              await _inventoryService.ReserveAsync(order); // Шаг 2: зарезервировать товар
              await _notificationService.SendConfirmationAsync(order); // Шаг 3: уведомить клиента
          }
          catch (Exception)
          {
              // А вот если где-то просрались — начинается компенсация (в обратном порядке, ясное дело)
              await _inventoryService.ReleaseAsync(order); // Отпускаем товар
              await _paymentService.RefundAsync(order); // Возвращаем деньги
              throw; // И всем показываем, что мы лохи
          }
      }
      }
  2. Хореография (Choreography):

    • Тут уже нету главного. Сервисы общаются через события в каком-нибудь брокере вроде Kafka или RabbitMQ, как на вечеринке: один что-то сделал — крикнул об этом, другой услышал — сделал своё дело и тоже крикнул.
    • Примерный поток событий: OrderPlaced (заказ создан) -> PaymentProcessed (платёж прошёл) -> InventoryReserved (товар зарезервирован). А если сервис инвентаря не смог ничего зарезервировать, он орет событие InventoryReservationFailed, и все начинают откатываться: PaymentRefunded и так далее.

Где же подвох, спросишь ты? А вот они, ключевые компромиссы, ебать:

  • Оркестрация — с ней проще, всё как на ладони, контролировать и мониторить удобно. Но она создаёт эту самую точку централизации, которая может стать бутылочным горлышком или единой точкой отказа, если накосячить.
  • Хореография — она более распределённая, гибкая, сервисы друг про друга почти не знают. Но вот отлаживать этот цирк и отслеживать, в каком состоянии находится весь процесс — это просто пиздец, иногда волосы дыбом встают.

Инструменты? Для простых сценариев — хватит и кода выше. Но если у тебя там процессы на три дня, с кучей ветвлений — тогда уже смотри в сторону специализированных фреймворков, которые состояние в БД сохраняют, или платформ типа Azure Durable Functions для той же оркестрации. Главное — не выеби себе мозг на ровном месте, выбирая сложность там, где можно обойтись парой событий.