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