Saga-паттерн — это архитектурный подход, используемый для управления распределенными транзакциями в микросервисных архитектурах. В отличие от традиционных ACID-транзакций, которые выполняются в рамках одной базы данных, Saga-паттерн позволяет управлять транзакциями, которые охватывают несколько микросервисов. Это достигается за счет разбиения транзакции на последовательность локальных транзакций, каждая из которых выполняется в своем микросервисе. Если одна из локальных транзакций завершается неудачно, Saga запускает компенсирующие транзакции (compensating transactions) для отката изменений.
Каждая Saga состоит из последовательности локальных транзакций, каждая из которых выполняется в своем микросервисе. Локальные транзакции должны быть атомарными и изолированными.
Если одна из локальных транзакций завершается неудачно, Saga запускает компенсирующие транзакции для отката изменений, сделанных предыдущими локальными транзакциями. Компенсирующие транзакции должны быть идемпотентными, чтобы их можно было безопасно повторять.
Saga может быть реализована с использованием двух основных подходов:
Рассмотрим пример реализации Saga с использованием оркестрации на Java.
import java.util.ArrayList;
import java.util.List;
public class SagaOrchestrator {
private List<SagaStep> steps = new ArrayList<>();
public void addStep(SagaStep step) {
steps.add(step);
}
public void execute() {
for (SagaStep step : steps) {
try {
step.execute();
} catch (Exception e) {
compensate(step);
throw e;
}
}
}
private void compensate(SagaStep failedStep) {
for (SagaStep step : steps) {
if (step == failedStep) {
break;
}
step.compensate();
}
}
public static void main(String[] args) {
SagaOrchestrator saga = new SagaOrchestrator();
saga.addStep(new ReserveHotelStep());
saga.addStep(new BookFlightStep());
saga.addStep(new ChargeCreditCardStep());
try {
saga.execute();
System.out.println("Saga completed successfully");
} catch (Exception e) {
System.out.println("Saga failed, compensation applied");
}
}
}
interface SagaStep {
void execute();
void compensate();
}
class ReserveHotelStep implements SagaStep {
public void execute() {
System.out.println("Hotel reserved");
// Логика резервирования отеля
}
public void compensate() {
System.out.println("Hotel reservation cancelled");
// Логика отмены резервирования отеля
}
}
class BookFlightStep implements SagaStep {
public void execute() {
System.out.println("Flight booked");
// Логика бронирования билета
}
public void compensate() {
System.out.println("Flight booking cancelled");
// Логика отмены бронирования билета
}
}
class ChargeCreditCardStep implements SagaStep {
public void execute() {
System.out.println("Credit card charged");
// Логика списания средств с кредитной карты
}
public void compensate() {
System.out.println("Credit card refunded");
// Логика возврата средств на кредитную карту
}
}
execute
и compensate
для локальных и компенсирующих транзакций.SagaStep
для выполнения и отката конкретных операций.Рассмотрим пример реализации Saga с использованием хореографии на Java.
import java.util.HashMap;
import java.util.Map;
public class SagaChoreography {
private Map<String, SagaStep> steps = new HashMap<>();
public void addStep(String stepName, SagaStep step) {
steps.put(stepName, step);
}
public void execute(String stepName) {
SagaStep step = steps.get(stepName);
if (step != null) {
try {
step.execute();
executeNext(stepName);
} catch (Exception e) {
compensate(stepName);
}
}
}
private void executeNext(String currentStepName) {
// Логика определения следующего шага
String nextStepName = determineNextStep(currentStepName);
if (nextStepName != null) {
execute(nextStepName);
}
}
private void compensate(String failedStepName) {
// Логика определения предыдущих шагов и их компенсации
String previousStepName = determinePreviousStep(failedStepName);
if (previousStepName != null) {
steps.get(previousStepName).compensate();
compensate(previousStepName);
}
}
private String determineNextStep(String currentStepName) {
// Логика определения следующего шага
return null; // Пример
}
private String determinePreviousStep(String currentStepName) {
// Логика определения предыдущего шага
return null; // Пример
}
public static void main(String[] args) {
SagaChoreography saga = new SagaChoreography();
saga.addStep("ReserveHotel", new ReserveHotelStep());
saga.addStep("BookFlight", new BookFlightStep());
saga.addStep("ChargeCreditCard", new ChargeCreditCardStep());
saga.execute("ReserveHotel");
}
}
execute
и compensate
для локальных и компенсирующих транзакций.SagaStep
для выполнения и отката конкретных операций.Saga-паттерн — это мощный инструмент для управления распределенными транзакциями в микросервисных архитектурах. Он позволяет разбивать транзакции на последовательность локальных транзакций и обеспечивает откат изменений в случае ошибки. Saga может быть реализована с использованием оркестрации или хореографии, что обеспечивает гибкость и масштабируемость. В Java Saga-паттерн может быть реализован с использованием классов и интерфейсов для выполнения и отката транзакций, что позволяет создавать надежные и масштабируемые системы.