Ми розглянули два основні підходи до координації Saga: оркестрація, коли є централізований координатор, і хореографія, коли мікросервіси обмінюються подіями напряму — як на вечірці, де всі танцюють без діджея. В цьому практичному занятті ми зануримось у реалізацію патерна Saga на практиці.
Бізнес-кейс: бронювання подорожі
Припустимо, ми робимо застосунок для туристичного агентства. Користувач хоче забронювати пакет "усе включено": переліт, готель і екскурсію. Однак кожен з цих кроків виконується через окремі мікросервіси:
- Flight Service — відповідає за бронювання авіаквитків.
- Hotel Service — обробляє бронювання номерів у готелі.
- Tour Service — керує замовленням екскурсій.
Наша задача — реалізувати процес бронювання через патерн Saga, щоб:
- У випадку успіху всіх кроків ми підтверджували бронювання.
- У разі невдачі на будь-якому етапі ми відкатували попередні кроки (скасовували бронь квитків, звільняли номери в готелі тощо).
Проєктування системи
Перед тим як поринути в код, давай розберемося, як виглядатиме процес на рівні взаємодії мікросервісів. Ми оберемо підхід оркестрації, щоб простіше координувати процес.
Кроки Saga
- Розпочати бронювання.
- Забронювати авіаквитки в Flight Service.
- Забронювати готель в Hotel Service.
- Забронювати екскурсію в Tour Service.
- Завершити бронювання при успіху.
- Скасувати всі раніше виконані дії у випадку помилки.
Діаграма взаємодії
Saga Orchestrator
|
|---> Flight Service (Reserve)
| |
| ---> Failure? --> Cancel Flight
|
|---> Hotel Service (Reserve)
| |
| ---> Failure? --> Cancel Hotel
|
|---> Tour Service (Reserve)
| |
| ---> Failure? --> Cancel Tour
|
---> Booking Confirmed!
Компенсаційні дії
Кожна дія в Saga має супроводжуватися можливістю відкату. Наприклад, якщо бронювання авіаквитків пройшло успішно, а бронювання готелю впало, нам потрібно скасувати бронь квитків.
Реалізація
Ми будемо використовувати Spring Boot і Spring Kafka для реалізації саги з оркестратором. Для простоти залишимо наші мікросервіси як емулятори (наприклад, прості REST-контролери).
Підготовка проєкту
Створимо три мікросервіси: Orchestrator, FlightService, HotelService, TourService.
Крок 1: Налаштування Orchestrator
Почнемо зі створення сервісу Orchestrator для управління сагою.
Додаємо залежності в pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
Створюємо структуру проєкту:
.
├── OrchestratorApplication.java
└── services/
├── FlightServiceClient.java
├── HotelServiceClient.java
└── TourServiceClient.java
SagaOrchestrator.java
@Service
public class SagaOrchestrator {
private final FlightServiceClient flightService;
private final HotelServiceClient hotelService;
private final TourServiceClient tourService;
public SagaOrchestrator(FlightServiceClient flightService, HotelServiceClient hotelService, TourServiceClient tourService) {
this.flightService = flightService;
this.hotelService = hotelService;
this.tourService = tourService;
}
public String processBooking() {
try {
// Step 1: book flight
String flightBookingId = flightService.reserveFlight();
System.out.println("Flight reserved with ID: " + flightBookingId);
// Step 2: book hotel
String hotelBookingId = hotelService.reserveHotel();
System.out.println("Hotel reserved with ID: " + hotelBookingId);
// Step 3: book tour
String tourBookingId = tourService.reserveTour();
System.out.println("Tour reserved with ID: " + tourBookingId);
return "Booking successful!";
} catch (Exception e) {
System.err.println("Booking failed: " + e.getMessage());
return "Booking failed and rolled back.";
}
}
}
Крок 2: Створення сервісів FlightService, HotelService, TourService
FlightServiceClient.java
@Component
public class FlightServiceClient {
public String reserveFlight() {
// Емуляція бронювання
System.out.println("Reserving flight...");
return UUID.randomUUID().toString(); // повертаємо фіктивний ID
}
public void cancelFlight(String bookingId) {
System.out.println("Flight reservation with ID " + bookingId + " cancelled.");
}
}
Аналогічно створіть HotelServiceClient та TourServiceClient.
Додавання Kafka для передачі подій
Тепер, щоб наші мікросервіси спілкувалися між собою, давай інтегруємо Kafka. Наприклад, FlightService буде відправляти події про підтвердження броні, а Orchestrator їх підхоплюватиме.
Налаштування KafkaProducer:
@Service
public class KafkaProducer {
private final KafkaTemplate<String, String> kafkaTemplate;
public KafkaProducer(KafkaTemplate<String, String> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
public void sendEvent(String topic, String message) {
kafkaTemplate.send(topic, message);
System.out.println("Event sent to topic: " + topic + ", message: " + message);
}
}
Orchestrator: обробка відповідей:
@KafkaListener(topics = "flight-status", groupId = "orchestrator")
public void listenFlightResponses(String message) {
System.out.println("Received flight status: " + message);
}
Ми побудували основи Saga з простим управлінням через мікросервіси. Наступні кроки включають додавання детальної обробки помилок і покращення логування через розподілені трасування.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ