Теперь мы находимся на этапе реализации микросервисов для обработки событий — того самого момента, когда магия становится реальностью (или, по крайней мере, стремится ею стать). Сегодня мы будем создавать небольшую, но функциональную систему обработки событий с использованием Spring Boot и Kafka. Готовы к кодингу? Тогда пристегнитесь: этот путь будет увлекательным.
Зачем нужны события и как они помогают микросервисам
Микросервисы, как и коллега в соседнем кубикле, должны взаимодействовать друг с другом. Вместо того чтобы напрямую обращаться к другому сервису (что бы создало излишнюю связанность), микросервисы используют события. Например, когда пользователь оформляет заказ в интернет-магазине, событие OrderCreated может быть отправлено в топик Kafka, чтобы другие микросервисы (например, склад, доставка и оплаты) могли обработать его.
Вот что делают события:
- Разрывают связь между сервисами: чтобы один сервис не "знал" слишком много про другой.
- Обеспечивают асинхронную обработку: ваш сервис не будет простаивать в ожидании ответа на запрос.
- Способствуют масштабируемости: количество сообщений можно распределять по нескольким консьюмерам.
Этап 1: Создание микросервиса
Начнем с создания простого микросервиса, который будет отправлять события в Kafka — это будет наш Order Service. Этот сервис будет принимать запросы на создание заказа и публиковать событие OrderCreated.
Шаг 1: Настройка проекта в Spring Boot
Создаём новый Spring Boot проект с такими зависимостями:
- Spring Web (для REST API)
- Spring Kafka (для интеграции с Kafka)
- Spring Boot Actuator (для мониторинга)
- Spring Boot DevTools (для упрощения разработки, необязательная опция)
Если используете Maven, добавьте вот такие зависимости в pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
Если у вас Gradle (привет, любители лаконичности YAML), используйте это:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.kafka:spring-kafka'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
}
Шаг 2: Конфигурация Kafka
Добавьте в application.yml конфигурацию для Kafka:
spring:
kafka:
bootstrap-servers: localhost:9092
producer:
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer
consumer:
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
Эта конфигурация указывает, где находится Kafka, и какие сериализаторы/десериализаторы использовать. Если вы ещё не настроили Kafka, вернитесь к лекции 185 для подробностей!
Шаг 3: Создание продюсера (отправка сообщений)
Создайте класс OrderProducer, который будет отправлять сообщения в Kafka:
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
@Service
public class OrderProducer {
private final KafkaTemplate<String, String> kafkaTemplate;
public OrderProducer(KafkaTemplate<String, String> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
public void sendOrderCreatedEvent(String orderId) {
kafkaTemplate.send("order-events", orderId);
System.out.println("Отправлено событие для заказа: " + orderId);
}
}
Здесь мы используем KafkaTemplate, который является частью Spring Kafka, для отправки сообщений в топик order-events.
Шаг 4: Создание REST-контроллера
Теперь добавьте REST-контроллер для обработки запросов на создание заказов:
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/orders")
public class OrderController {
private final OrderProducer orderProducer;
public OrderController(OrderProducer orderProducer) {
this.orderProducer = orderProducer;
}
@PostMapping("/{orderId}")
public String createOrder(@PathVariable String orderId) {
orderProducer.sendOrderCreatedEvent(orderId);
return "Заказ создан: " + orderId;
}
}
Этот контроллер принимает POST запросы для создания заказов и публикует событие через продюсер.
Проверка
Запустите ваше приложение и отправьте POST запрос через Postman или CURL:
curl -X POST http://localhost:8080/orders/12345
Если вы видите в логах Отправлено событие для заказа: 12345, значит, всё работает. Осталось обработать это событие на другой стороне.
Этап 2: Обработка событий в другом микросервисе
Теперь создадим другой микросервис, который будет слушать топик order-events. Этот сервис, скажем, будет называться Inventory Service и обновит остатки на складе для созданного заказа.
Шаг 1: Настройка консьюмера
В Inventory Service добавьте зависимость spring-kafka точно так же, как мы сделали это в Order Service. Добавьте следующую конфигурацию в application.yml:
spring:
kafka:
bootstrap-servers: localhost:9092
consumer:
group-id: inventory-service
auto-offset-reset: earliest
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
Шаг 2: Создание консьюмера
Создайте класс OrderConsumer, который будет слушать сообщения:
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;
@Service
public class OrderConsumer {
@KafkaListener(topics = "order-events", groupId = "inventory-service")
public void processOrderCreatedEvent(String orderId) {
System.out.println("Получено событие для заказа: " + orderId);
// Здесь добавьте бизнес-логику, например, обновление остатков
}
}
Аннотация @KafkaListener указывает, что этот метод будет вызван для каждого сообщения из топика order-events.
Проверка
Запустите сначала Kafka, затем оба микросервиса. После этого отправьте POST запрос снова. Вы должны увидеть в логах Inventory Service сообщение: Получено событие для заказа: 12345.
Этап 3: Обеспечение целостности
Когда мы говорим о событиях, важно понимать, что сообщения могут теряться или дублироваться. Чтобы этого не происходило:
- Убедитесь, что ваш Kafka топик настроен с "At least once" гарантией.
- Реализуйте логику идемпотентности в потребителе. Например, храните обработанные события в базе данных, чтобы не обрабатывать их дважды.
Теперь у вас есть рабочая система с двумя микросервисами, которые обмениваются событиями через Kafka. Это базис для событийно-ориентированных архитектур! В следующей лекции мы продолжим наши приключения, добавив дополнительные компоненты для мониторинга и логирования.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ