Классический подход к интеграции сервисов — синхронные запросы. Один сервис вызывает другой, ждёт ответа и идёт дальше. Работает хорошо, пока у тебя монолит или два-три сервиса. Но как только система разрастается, начинаются проблемы: цепочки зависимостей, один упавший сервис кладёт всё остальное, под нагрузкой всё тормозит.

Event-driven архитектура (EDA) решает это иначе. Вместо прямых вызовов сервисы публикуют события — «заказ создан», «платёж прошёл», «пользователь зарегистрировался» — и подписываются на чужие события. Никто никого не ждёт. Кто хочет — реагирует, кто не нужен — молчит.

Это как Telegram-канал: ты публикуешь пост, подписчики читают в удобное время. Ты не обзваниваешь каждого лично.

Три инструмента — Kafka, RabbitMQ, Redis Streams

Самые популярные инструменты для реализации EDA — Apache Kafka, RabbitMQ и Redis Streams. Они похожи по назначению, но сильно отличаются по философии и сценариям использования.

Apache Kafka

Kafka — это не просто брокер сообщений. Это распределённый лог. Сообщения хранятся на диске и не удаляются после прочтения (по умолчанию — 7 дней). Это позволяет любому потребителю «перемотать» историю назад и переобработать события.

Kafka работает с топиками, которые делятся на партиции. Каждая партиция — упорядоченная очередь с уникальным смещением (offset) для каждого сообщения. Потребители читают из партиций независимо и помнят, где остановились.

Где Kafka выигрывает:

  • Аналитика в реальном времени — кликстримы, метрики, логи
  • Event sourcing, когда история событий важна
  • Высокая пропускная способность — миллионы сообщений в секунду на нормальном железе
  • Несколько независимых потребителей читают один поток

Слабые стороны:

  • Тяжёлая инфраструктура. Kafka требует ZooKeeper (или KRaft в новых версиях), минимум 3 брокера для продакшена — это уже несколько серверов
  • Сложная настройка: репликация, retention, consumer groups, lag — нужно понимать, что делаешь
  • Для задач «отправил и забыл» это слишком много

Типичный сценарий: интернет-магазин фиксирует каждое действие пользователя. Kafka хранит поток событий. Один потребитель считает аналитику, второй обновляет рекомендации, третий пишет в data warehouse — всё независимо.

RabbitMQ

RabbitMQ — классический брокер сообщений с богатой моделью маршрутизации. Здесь есть exchanges (точки обмена) и queues (очереди). Сообщение приходит на exchange, тот по правилам (direct, topic, fanout, headers) раскидывает его по очередям, потребители читают из очередей.

После того как сообщение прочитано и подтверждено (acknowledgement), оно удаляется. Kafka хранит историю — RabbitMQ нет.

Где RabbitMQ выигрывает:

  • Задачи, требующие гарантированной доставки и подтверждений
  • Сложная маршрутизация — «отправь это в такую-то очередь только если флаг priority=high»
  • Долго живущие задачи: обработка видео, генерация отчётов
  • Небольшие команды — быстрее поднять и освоить

Слабые стороны:

  • Не для высокопроизводительного стриминга — Kafka здесь на порядок мощнее
  • Нет нативного replay событий
  • При больших нагрузках очереди могут раздуться, если потребители не успевают

Типичный сценарий: сервис отправки email. Пришёл заказ — сообщение пошло в очередь. Email-сервис забрал, отправил письмо, подтвердил получение. Если что-то упало — сообщение вернётся в очередь.

Redis Streams

Redis Streams появились в Redis 5.0 как ответ на Kafka — проще и без отдельной инфраструктуры, если Redis у тебя уже есть.

Streams — это append-only лог внутри Redis. Каждая запись имеет уникальный ID (timestamp + порядковый номер). Потребительские группы (consumer groups) позволяют нескольким воркерам читать поток параллельно, каждый получает свою порцию сообщений.

Где Redis Streams выигрывают:

  • Уже используешь Redis — не надо поднимать новый сервис
  • Лёгкие задачи реального времени: уведомления, live-фиды, чаты
  • Нужен лог событий, но не в масштабах Kafka
  • Прототипирование — быстро поднять и проверить идею

Слабые стороны:

  • Данные в памяти (RAM) — дороже хранить большие объёмы
  • Нет сложной маршрутизации как в RabbitMQ
  • Не заменяет Kafka на серьёзных объёмах

Типичный сценарий: система уведомлений. Произошло событие — запись в стрим. Несколько воркеров читают стрим и шлют push-уведомления, email или SMS в зависимости от типа.

Как выбрать нужный инструмент

Хороший способ — задать три вопроса.

Нужна ли история событий? Если да — Kafka. Когда важно переобрабатывать старые события, строить аналитику, реализовывать event sourcing — Kafka с её логом незаменима.

Нужна ли сложная маршрутизация и гарантии доставки? Если да — RabbitMQ. Когда важно «это сообщение должно дойти, и вот правила, по каким очередям его разослать» — RabbitMQ делает это лучше всех.

У тебя уже есть Redis и задача несложная? Redis Streams. Не надо усложнять то, что работает просто.

Конкретные ориентиры по нагрузке:

  • До 10 000 сообщений/сек — Redis Streams или RabbitMQ справятся
  • 10 000 — 1 000 000 сообщений/сек — Kafka
  • Выше 1 млн/сек — нужна серьёзная настройка Kafka с шардированием

Паттерны event-driven архитектуры

Понять технологию — это половина дела. Важнее понять, как правильно проектировать систему.

Event notification. Самый простой паттерн. Сервис публикует событие как факт: «пользователь обновил профиль». Получатели реагируют как хотят. Никакого контракта — хочешь знать детали, сходи в API источника. Минус: если получателей много и каждый лезет в API за деталями, это создаёт лишнюю нагрузку.

Event-carried state transfer. Событие несёт всё необходимое: «пользователь обновил профиль, вот новые данные». Получателям не нужно никуда ходить. Минус — события становятся больше, и если схема данных изменится, надо обновлять всех потребителей.

Event sourcing. Система хранит не текущее состояние, а историю всех событий. Текущее состояние — это «replay» событий от начала. Позволяет вернуться в любой момент времени. Паттерн сложный в реализации, но мощный — хорошо подходит для финансовых систем и аудита.

CQRS (Command Query Responsibility Segregation). Часто идёт в паре с event sourcing. Разделяешь записи (commands) и чтения (queries). Один сервис принимает команды и генерирует события, другой строит проекции для чтения. Kafka здесь — естественный клей.

Подводные камни, о которых не предупреждают

Дублирование сообщений. Ни Kafka, ни RabbitMQ не гарантируют exactly-once без дополнительной работы. Сеть упала в момент acknowledgement — сообщение придёт снова. Твои потребители должны быть идемпотентны: обработать одно событие дважды не должно быть катастрофой.

Порядок сообщений. В Kafka порядок гарантирован только внутри партиции. Если используешь несколько партиций, события от одного пользователя могут прийти в разном порядке. Решение — partition key: всё что связано с одним пользователем пишется в одну партицию.

Схема данных. Рано или поздно понадобится изменить структуру события. Без продуманного версионирования получишь несовместимость между старыми и новыми потребителями. Schema Registry для Kafka или ручное версионирование — обязательно с самого начала.

Отладка. Синхронные системы легко отлаживать: запрос пришёл, ответ ушёл, в логах всё видно. В event-driven системе проследить путь события через несколько сервисов — задача. Без distributed tracing (Jaeger, Zipkin) будешь страдать.

Dead letter queue. Что делать, если потребитель не может обработать сообщение? RabbitMQ поддерживает DLQ из коробки — упавшие сообщения летят в отдельную очередь. В Kafka надо реализовывать самому. Не забудь об этом заранее.

Инфраструктура и стоимость

Kafka — самый дорогой вариант по инфраструктуре. Минимальный продакшен-кластер: 3 брокера + 3 ZooKeeper-ноды. Управляемые решения (Confluent Cloud, AWS MSK, Yandex Managed Kafka) снижают операционную нагрузку, но стоят дороже.

RabbitMQ проще: один узел тянет тысячи сообщений в секунду. Для высокой доступности — кластер из 3 нод.

Redis Streams — если Redis уже есть, добавочных расходов ноль. Самый дешёвый старт.

Когда проектируешь API с event-driven бэкендом, выбор брокера влияет на всю архитектуру. В REEXY при разработке сложных интеграций помогают выбрать стек под конкретную задачу — иногда достаточно Redis Streams, иногда без Kafka не обойтись. Интеграция сервисов — от 1 500 ₽.

Когда event-driven не нужен

EDA не серебряная пуля. Вот когда лучше остаться на синхронных вызовах:

  • Маленький проект, 1-2 сервиса — усложнять ни к чему
  • Нужен немедленный ответ пользователю (оплата прошла или нет) — синхронный запрос понятнее
  • Команда не знакома с брокерами — затраты на обучение могут не окупиться
  • Бюджет ограничен, а отдельный сервис для брокера дорого — сначала убедись, что EDA решает реальную боль

Хорошее правило: если сервисов меньше пяти и они общаются нечасто — синхрон. Как только начинается «у нас 10 сервисов и они постоянно дёргают друг друга» — пора смотреть в сторону событийной архитектуры.

Быстрый старт

Redis Streams — проще всего. Запускаешь Redis (docker run -d redis), пишешь XADD для публикации и XREAD/XREADGROUP для чтения. Без лишних зависимостей.

RabbitMQ — docker-compose с одной нодой, официальный образ rabbitmq:management даёт веб-интерфейс на порту 15672. Библиотеки есть для всех языков: pika (Python), amqplib (Node.js), spring-amqp (Java).

Kafka — docker-compose с Kafka + ZooKeeper или новый KRaft-режим без ZooKeeper. Попробуй Redpanda как Kafka-совместимую альтернативу — она проще в setup и работает в один бинарник.

Начни с примера Producer → Topic → Consumer. Почувствуй задержки, посмотри на consumer lag, поиграй с partition key. После этого архитектурные решения будут даваться намного легче.