JavaRush /Курсы /Модуль 5. Spring /Лекция 204: Когда использовать события: подходы и лучшие ...

Лекция 204: Когда использовать события: подходы и лучшие практики

Модуль 5. Spring
21 уровень , 3 лекция
Открыта

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

Они позволяют системе работать асинхронно и обособленно. Например:

  • Один сервис может сообщить другому сервису об изменении состояния.
  • Система может выстраивать цепочки действий, где одно событие инициирует следующее.

Когда использовать события в EDA?

События идеально подходят для задач, где:

  1. Слабая связанность компонентов критична. Это позволяет системе быть устойчивой и модульной.
  2. Асинхронная обработка данных предпочтительнее синхронного вызова API.
  3. Работа с большим объёмом данных, которые необходимо обработать пошагово. Например, обработка заказов в e-commerce или обновление состояния системы.
  4. Необходимость в scalability (масштабируемости). События помогают распределять нагрузку между микросервисами.
  5. Вы хотите, чтобы ваши компоненты работали независимо, даже если один из них временно не доступен.

Как применять события? Практические примеры

Уведомления и события

Допустим, у вас есть e-commerce приложение. Когда пользователь оформляет заказ, это действие может запустить несколько процессов:

  • Отправка уведомления о заказе.
  • Списание товара со склада.
  • Упаковка и доставка.

Здесь событие "Заказ оформлен" выступает в роли триггера. Компоненты взаимодействуют через события, а не напрямую, что делает их независимыми.

Пример:


// Событие, инициируемое при оформлении заказа
public class OrderCreatedEvent {
    private final String orderId;
    private final LocalDateTime timestamp;

    public OrderCreatedEvent(String orderId) {
        this.orderId = orderId;
        this.timestamp = LocalDateTime.now();
    }

    public String getOrderId() {
        return orderId;
    }

    public LocalDateTime getTimestamp() {
        return timestamp;
    }
}


// Продюсер: создаёт событие и отправляет его
@Component
public class OrderEventProducer {

    private final KafkaTemplate<String, OrderCreatedEvent> kafkaTemplate;

    public OrderEventProducer(KafkaTemplate<String, OrderCreatedEvent> kafkaTemplate) {
        this.kafkaTemplate = kafkaTemplate;
    }

    public void publishOrderEvent(OrderCreatedEvent event) {
        kafkaTemplate.send("orders-topic", event);
        System.out.println("Order event sent: " + event.getOrderId());
    }
}


// Подписчик: обрабатывает событие
@Component
@KafkaListener(topics = "orders-topic", groupId = "order-group")
public class NotificationService {

    @KafkaHandler
    public void handleOrderEvent(OrderCreatedEvent event) {
        System.out.println("Sending notification for order: " + event.getOrderId());
    }
}

Асинхронное обновление состояния

Допустим, вы создаёте приложение с системой для отслеживания доставки. Службы доставки работают независимо, но обновляют статус в центральной системе.

  1. Каждая служба отправляет событие "Доставка обновлена".
  2. Централизованный сервис обрабатывает события и обновляет базу данных.

Такой подход уменьшает нагрузку на центральный сервис и ускоряет обработку.


Шаблоны проектирования для событий

Шаблон "Best-Effort Messaging". Этот подход не гарантирует доставку сообщения, но подходит для задач, где потеря сообщения не критична. Например, логирование событий.

Шаблон "Guaranteed Delivery". Если событие должно быть доставлено обязательно, используйте библиотеки или брокеры сообщений, которые гарантируют доставку хотя бы один раз ("At Least Once"). Например, Kafka успешно справляется с этой задачей, используя репликацию сообщений.

Шаблон "Dead Letter Queue (DLQ)". Что делать, если сообщение не может быть обработано? Вы можете отправить его в DLQ — очередь для "проблемных" сообщений. Это позволяет следить за ошибками и их исправлением.


Подходы к оптимизации событийного подхода

* Использование событий с минимальной нагрузкой*

Событие должно нести только необходимую информацию. Например, событие "Заказ создан" не должно содержать 10 МБ данных о заказе. Достаточно идентификатора заказа.

Плохой пример:

{
    "orderId": "12345",
    "customerData": { "name": "Иван Иванов", "email": "ivanov@example.com" },
    "products": [
        { "id": "prod1", "name": "Телефон", "quantity": 1 },
        { "id": "prod2", "name": "Чехол", "quantity": 2 },
        ...
    ]
}

Лучший вариант:

{
    "orderId": "12345",
    "timestamp": "2023-10-20T12:00:00"
}

Иерархия событий

События часто имеют родственные связи. Например:

  • Событие "Платеж обработан" может породить "Платеж подтвержден" или "Платеж отклонен".
  • Правильная структуризация событий позволит упростить их обработку.

Советы и практические рекомендации

  1. Не создавайте слишком сложную логику для обработки событий. Простота — залог успеха.
  2. Соблюдайте формат событий. JSON или Avro помогут стандартизировать сообщения.
  3. Регулярно мониторьте события. Используйте такие инструменты, как Grafana или ELK Stack, чтобы отслеживать, что происходит с вашими событиями.
  4. Подумайте о производительности брокера сообщений. Например, Kafka поддерживает тысячи событий в секунду — это хорошо подходит для высоконагруженных систем.
  5. Документируйте события. Опишите, что означает каждое событие, чтобы облегчить работу другим разработчикам.

Когда не стоит использовать события?

EDA — это не серебряная пуля. Если ваша система требует строгой последовательности выполнения и синхронности, например, при обработке банковских транзакций, события могут добавить излишнюю сложность. Для таких задач лучше подходят синхронные вызовы (REST, gRPC).


Таким образом, использование событий помогает строить высокопроизводительные, гибкие и масштабируемые системы. Важно помнить, что события — это инструмент, и применять его стоит с умом. В следующей лекции мы начнем разбирать инструменты, которые помогут эффективно работать с EDA, включая Kafka, RabbitMQ и ActiveMQ.

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ