Брокеры сообщений — Kafka, RabbitMQ, SQS

Урок 6 из 8

Урок 07.06: Брокеры сообщений — Kafka, RabbitMQ, SQS

Цель урока

Понять фундаментальные модели работы брокеров сообщений (Queue и Log-based), научиться выбирать брокер под задачу, проектировать топики, очереди, ключи партиционирования и схемы dead letter queue. Особый фокус: что должен знать и уметь аналитик при проектировании асинхронной интеграции с брокером, чтобы не допустить фатальных ошибок (потеря сообщений, нарушение порядка, неверный выбор брокера).


Ключевые понятия

Термин Определение
Брокер сообщений (Message Broker) Промежуточное ПО для асинхронной передачи сообщений между системами
Producer (Продюсер) Приложение, которое отправляет сообщения в брокер
Consumer (Консюмер) Приложение, которое читает сообщения из брокера
Topic (Топик) Именованный канал (категория) сообщений в Kafka/Pulsar
Partition (Партиция) Единица параллелизма в Kafka — упорядоченная последовательность сообщений
Offset Порядковый номер сообщения внутри партиции Kafka
Consumer Group Группа потребителей, совместно читающих топик (каждая партиция — одному consumer)
Queue (Очередь) Канал «точка-точка»: сообщение получает ровно один consumer (RabbitMQ, SQS)
Exchange (Обменник) Маршрутизатор в RabbitMQ: определяет, в какие очереди попадает сообщение
Routing Key Ключ маршрутизации — строка, которую exchange сравнивает с binding
Binding Правило: exchange → очередь (набор routing key patterns)
DLQ (Dead Letter Queue) Очередь для «проблемных» сообщений, которые не смогли обработаться
Retention Время/размер хранения сообщений в брокере (Kafka хранит долго, RabbitMQ — до ACK)
Replay Возможность перечитать старые сообщения (Kafka — да, RabbitMQ — нет)
Consumer Lag Отставание consumer-а от producer-а (измеряется в сообщениях или времени)
Rebalancing Перераспределение партиций между consumer-ами при изменении состава группы
Poison Message Сообщение, которое consumer не может обработать (снова и снова)
At-least-once / At-most-once / Exactly-once Гарантии доставки (подробно в уроке 07.05)

1. Зачем нужны брокеры сообщений?

1.1. Проблема прямой интеграции «система-система»

Представьте, что три сервиса должны обмениваться данными напрямую, без брокера:

     ┌──────────┐       HTTP       ┌──────────┐
     │  Сервис A │────────────────→│  Сервис B │
     │  (Order)  │                 │  (Payment)│
     └──────────┘                  └──────────┘
           │                            │
           │ HTTP                       │ HTTP
           ▼                            ▼
     ┌──────────┐                  ┌──────────┐
     │  Сервис C │                  │  Сервис D │
     │ (Notify)  │                  │(Analytics)│
     └──────────┘                  └──────────┘

Проблемы такого подхода:

Проблема Описание Последствие
Связанность (Coupling) Сервис A знает про B, C, D — и их адреса Изменение C (новый URL) → меняется A
Надёжность Если D упал — A получает timeout и ошибку A должен обрабатывать ошибки каждого сервиса
Масштабирование При пике нагрузки A должен ждать, пока B обработает A блокируется, растёт latency
Гарантии доставки Если D упал на момент отправки — сообщение потеряно Нет встроенного retry
Один ко многим A должен разослать одно событие всем подписчикам N HTTP-запросов из A
Трассировка Нет единого места, где виден поток сообщений Отладка — ад

1.2. Решение: брокер сообщений

                         ┌───────────────┐
     ┌──────────┐        │               │        ┌──────────┐
     │  Сервис A │───────→│               │───────→│  Сервис B │
     │  (Order)  │        │   БРОКЕР      │        │ (Payment) │
     └──────────┘        │  СООБЩЕНИЙ    │        └──────────┘
                          │               │
     ┌──────────┐        │  (Kafka /      │        ┌──────────┐
     │  Сервис A │        │   RabbitMQ /  │        │  Сервис C │
     │  (Order)  │───────→│   SQS)       │───────→│ (Notify)  │
     └──────────┘        │               │        └──────────┘
                          │               │        ┌──────────┐
                          │               │───────→│ Сервис D │
                          └───────────────┘        │(Analytics)│
                                                    └──────────┘

Что даёт брокер:

Преимущество Как это работает
Слабая связанность Producer не знает consumer-ов. Он просто публикует сообщение в topic/queue
Надёжность Сообщение хранится в брокере, пока consumer не подтвердит обработку
Буферизация При пике нагрузки сообщения копятся в брокере, consumer обрабатывает в своём темпе
Один-to-многие Одно сообщение — N подписчиков (Pub-Sub)
Гарантии доставки At-least-once, at-most-once, exactly-once (effectively)
Retry и DLQ Ошибочные сообщения можно повторять или отправлять в DLQ
Аудит и replay Kafka хранит историю сообщений — можно перечитать

1.3. Аналогия: брокер — почтовое отделение

Без брокера:
  Вы (Producer) → идёте к каждому получателю лично → 
  если получателя нет дома → письмо не доставлено

С брокером (почтовое отделение):
  Вы → опускаете письмо в ящик (Producer → Topic)
  Почтальон (Consumer) → забирает и доставляет
  Если получателя нет → письмо на почте (DLQ после N попыток)
  Вы не знаете, кто и когда заберёт письмо (слабая связанность)
  Письмо хранится на почте N дней (retention)

2. Две фундаментальные модели брокеров

Все брокеры сообщений делятся на два принципиально разных типа:

2.1. Message Queue Model (Очередь) — RabbitMQ, AWS SQS

Producer ──→ Queue ──→ Consumer A (получает сообщение)
                 ──→ Consumer B (ждёт, пока A занят)
                 ──→ Consumer C (ждёт)

Как работает:

  • Одно сообщение → ровно один consumer (competitive consumption)
  • После ACK — сообщение удаляется из очереди
  • FIFO внутри очереди (если не настроено иначе)
  • Очередь — это временный буфер (сообщения живут, пока не обработаны или не истёк TTL)

Аналогия: Касса в супермаркете. Один покупатель → один кассир. После обслуживания покупатель уходит.

Когда выбирать:

  • Задача должна быть выполнена ровно один раз (send email, charge payment)
  • Нужна балансировка нагрузки между воркерами
  • Не нужно перечитывать историю

2.2. Log-based Model (Журнал / Event Log) — Apache Kafka, Pulsar

Producer ──→ Topic (Partition 0): [msg1, msg2, msg3, msg4, msg5, ...]
                ↓      ↓
          Consumer  Consumer
          Group A   Group B
          (читают   (читают
           всё)     всё)

Как работает:

  • Сообщения не удаляются после чтения — хранятся N дней (retention)
  • Каждый consumer group читает все сообщения независимо (Pub-Sub)
  • Consumer сам управляет offset (какое сообщение читать)
  • Partition — единица параллелизма и порядка

Аналогия: Лента в Instagram. Вы (producer) опубликовали пост. Все подписчики (consumer groups) увидят его независимо и в любое время (пока пост не удалён по retention).

Когда выбирать:

  • Одно событие нужно нескольким сервисам (Pub-Sub)
  • Нужна история сообщений (replay, аудит)
  • Высокая пропускная способность (миллионы сообщений/сек)
  • Важен строгий порядок внутри сущности (через ключ партиционирования)

2.3. Сравнение моделей

Характеристика Queue (RabbitMQ/SQS) Log-based (Kafka/Pulsar)
Модель Точка-точка Pub-Sub + Queue (через Consumer Group)
Удаление сообщений После ACK По retention (дни/недели)
Порядок Внутри очереди (FIFO) Внутри партиции
Пропускная способность Тысячи/сек Миллионы/сек
Хранение В оперативной памяти / диск (до ACK) На диске (журнал)
Replay ❌ Нет ✅ Да (reset offset)
Pub-Sub Через Exchange (RabbitMQ) Через Consumer Group
Сложность Низкая Высокая
Типичный use case Задачи (email, payment), worker distribution Event-driven архитектура, аудит, аналитика

Для аналитика: Это самый важный выбор в проектировании асинхронной интеграции. Kafka — не «улучшенный RabbitMQ». Это принципиально другая модель. Kafka хранит всё, RabbitMQ — только пока не обработано.


3. Apache Kafka — глубокое погружение

3.1. Базовая архитектура

                    ┌───────────────────┐
                    │    ZooKeeper /     │
                    │    KRaft (metadata)│
                    └────────┬──────────┘
                             │
┌──────────┐                 │          ┌──────────┐
│ Producer │─────────────────┼─────────→│  Kafka   │
│(Сервис А)│                 │          │  Cluster │
└──────────┘                 │          │          │
                              │          │ ┌──────┐ │
┌──────────┐                 │          │ │Part.0 │ │
│ Producer │─────────────────┼─────────→│ │Part.1 │ │
│(Сервис Б)│                 │          │ │Part.2 │ │
└──────────┘                 │          │ └──────┘ │
                              │          └────┬─────┘
                                         │
                    ┌────────────────────┼────────────────────┐
                    │                    │                    │
              ┌─────▼─────┐       ┌─────▼─────┐       ┌─────▼─────┐
              │ Consumer   │       │ Consumer   │       │ Consumer   │
              │ Group A    │       │ Group B    │       │ Group C    │
              │ (Service)  │       │(Analytics) │       │ (Audit)    │
              └───────────┘       └───────────┘       └───────────┘

3.2. Topic, Partition, Offset — «Святая Троица» Kafka

Topic — именованный канал сообщений (например, order.events, task.events).

Partition — единица параллелизма. Topic может иметь N партиций. Сообщения распределяются по партициям по ключу.

Topic: order.events (3 партиции)
┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐
│ Partition 0      │  │ Partition 1      │  │ Partition 2      │
│ offset: 0..999   │  │ offset: 0..999   │  │ offset: 0..999   │
│ key: order_1     │  │ key: order_2     │  │ key: order_5     │
│ key: order_4     │  │ key: order_3     │  │ key: order_7     │
│ ...              │  │ ...              │  │ ...              │
│ СТРОГИЙ ПОРЯДОК  │  │ СТРОГИЙ ПОРЯДОК  │  │ СТРОГИЙ ПОРЯДОК  │
│ ВНУТРИ partition │  │ ВНУТРИ partition │  │ ВНУТРИ partition │
└──────────────────┘  └──────────────────┘  └──────────────────┘

Offset — порядковый номер сообщения внутри партиции. Consumer запоминает offset, до которого он прочитал.

Consumer читает Partition 0:
   offset 0: order.created     {orderId: 1, status: "new"}
   offset 1: payment.processed {orderId: 1, status: "paid"}     ← Consumer сейчас здесь (offset=1)
   offset 2: order.confirmed   {orderId: 1, status: "confirmed"}
   offset 3: order.shipped     {orderId: 1, status: "shipped"}

3.3. Ключ партиционирования — самый важный проектный выбор

// Producer отправляет сообщение с ключом
producer.send(new ProducerRecord<>("order.events", 
    orderId,      // КЛЮЧ (определяет partition)
    event         // СООБЩЕНИЕ
));

// partition = hash(key) % numberOfPartitions

Таблица правильных и неправильных ключей:

Ключ Правильно? Результат
orderId (UUID заказа) Все события по одному заказу — в одной партиции → строгий порядок
taskId (UUID задачи) Все изменения задачи — последовательно
userId Все события пользователя — по порядку
random UUID Каждое сообщение — в случайную партицию → порядок не гарантирован
eventType («created», «updated») Все created — в Partition 0, все deleted — в Partition 1. Deleted может прийти раньше created
null Round-Robin распределение → никакого порядка

3.4. Consumer Group — масштабирование потребления

Topic: order.events (4 партиции)

Consumer Group "order-service" (2 consumers):
   Consumer A ← Partition 0, Partition 1
   Consumer B ← Partition 2, Partition 3

Consumer Group "analytics" (4 consumers):
   Consumer C ← Partition 0
   Consumer D ← Partition 1
   Consumer E ← Partition 2
   Consumer F ← Partition 3

Consumer Group "audit" (1 consumer):
   Consumer G ← Partition 0, Partition 1, Partition 2, Partition 3
                              (все партиции — одному consumer-у)

Правила:

  • Одна партиция → одному consumer-у в группе (нельзя 2 consumer-ам читать 1 партицию)
  • Если consumer-ов больше, чем партиций — лишние простаивают
  • Если consumer-ов меньше — один consumer читает несколько партиций
  • Разные Consumer Group читают независимо (каждая со своим offset)

Что должен спроектировать аналитик:

Параметр Вопрос Пример
Количество партиций Сколько сообщений в секунду? 100 msg/s → 3-6 партиций
Количество consumer-ов Сколько инстансов сервиса? 3 инстанса → 3+ партиции
Retention Как долго хранить сообщения? 7 дней (или 30 для аудита)
Ключ Как гарантировать порядок? orderId

3.5. Retention и Compaction

Retention — политика хранения сообщений:

Политика Параметр Пример
По времени retention.ms 7 дней (604800000 ms)
По размеру retention.bytes 10 GB на партицию
Compact (по ключу) cleanup.policy=compact Хранить только последнее сообщение по каждому ключу

Log Compaction — важная фича для аналитика:

До compaction:
  Partition 0:
    offset 0: {userId: 1, email: "old@mail.com"}
    offset 1: {userId: 2, email: "test@mail.com"}
    offset 2: {userId: 1, email: "new@mail.com"}  ← новое значение для userId=1

После compaction (cleanup.policy=compact):
  Partition 0:
    offset 1: {userId: 2, email: "test@mail.com"}
    offset 2: {userId: 1, email: "new@mail.com"}  ← осталось только последнее значение
    (сообщение с offset=0 удалено — устаревшее)

Где используется: Таблица «user profile» в Kafka — храним только последний профиль каждого пользователя. Новый consumer получает только актуальные данные.

3.6. Replication и ISR (In-Sync Replicas)

Kafka — распределённая система. Каждая партиция реплицируется на N брокеров:

Topic: order.events, Partition 0
  ┌─────────────────────────────────────────────┐
  │ Брокер 1 (Leader)                           │
  │ Partition 0: [msg1, msg2, msg3]             │
  │   ↓ репликация                               │
  │ Брокер 2 (Follower, in ISR)                 │
  │ Partition 0: [msg1, msg2, msg3]             │
  │   ↓ репликация                               │
  │ Брокер 3 (Follower, in ISR)                 │
  │ Partition 0: [msg1, msg2, msg3]             │
  └─────────────────────────────────────────────┘

ISR (In-Sync Replicas) — реплики, которые «успевают» за лидером.

Настройка durability (acks):

acks Что означает Риск Производительность
0 Producer не ждёт подтверждения ❌ Потеря сообщения при сбое 🚀 Максимальная
1 Ждёт подтверждения от лидера ⚠️ Потеря при падении лидера до репликации 🟡 Средняя
all (-1) Ждёт подтверждения от всех ISR ✅ Нет потери 🐢 Минимальная

Для аналитика: В требованиях к интеграции указывайте acks=all для критичных данных (заказы, платежи) и acks=1 для менее критичных (логи, метрики).

3.7. Когда Kafka — плохой выбор

Сценарий Почему Kafka не подходит Что выбрать
Нужна отложенная задача на 24 часа (Scheduled) Kafka не умеет откладывать сообщения RabbitMQ (TTL) + DLQ, или SQS (Delay Queue)
Нужно гарантировать exactly-once к конкретному моменту Kafka — at-least-once + идемпотентность (effectively once) RabbitMQ + dedup, или транзакционная БД
Сообщений мало (10/день) и не нужен replay Kafka — избыточен (кластер из 3 брокеров) RabbitMQ, SQS
Нужна строгая глобальная FIFO (все сообщения по порядку) Kafka — порядок только внутри партиции RabbitMQ (одна очередь, один consumer)
Нужна сложная маршрутизация (по content-based routing) Kafka — только по ключу в партицию RabbitMQ (Exchange с routing key)
Команда маленькая, нет опыта администрирования Kafka Kafka — сложный в эксплуатации Управляемые сервисы (Confluent Cloud, Redpanda) или SQS

4. RabbitMQ — глубокое погружение

4.1. Базовая архитектура

RabbitMQ основан на модели Exchange-Queue-Binding:

Producer ──→ Exchange ──(binding)──→ Queue ──→ Consumer
                 │                        │
                 │                   (если не обработано)
                 │                        │
                 └──(routing key)──→ DLQ (Dead Letter Queue)

Компоненты:

Компонент Описание Аналог в Kafka
Exchange Маршрутизатор: получает сообщение от producer и отправляет в очередь(и) Topic (но в Kafka producer пишет напрямую в partition)
Binding Правило: exchange → очередь (с routing key)
Queue Очередь сообщений (FIFO) Partition (но queue — временная, partition — постоянная)
Routing Key Строка, по которой exchange решает, в какую очередь отправить Ключ партиционирования
Consumer Приложение, читающее из очереди Consumer Group
DLQ Очередь для «плохих» сообщений — (в Kafka — через consumer retry topic)

4.2. Exchange Types — четыре типа маршрутизации

Direct Exchange — точное совпадение routing key

Producer → Exchange: direct → Queue A (routing key = "error")
                            → Queue B (routing key = "warning")
                            → Queue C (routing key = "info")

Сообщение с routing key = "error" → только в Queue A
Сообщение с routing key = "warning" → только в Queue B

Когда использовать: Логи (error → в алерт-систему, info → в архив), распределение задач по типу.

Topic Exchange — маска routing key (wildcard)

Producer → Exchange: topic → Queue A (routing key = "logs.error.*")
                           → Queue B (routing key = "logs.#")

Сообщение с routing key = "logs.error.db" → Queue A + Queue B
Сообщение с routing key = "logs.info.api" → Queue B (только B, не подходит под A)

# — любое количество слов (logs.# = logs.error.db, logs.info.api, logs.warning...)
* — ровно одно слово (logs.error.* = logs.error.db, НО НЕ logs.error.db.mysql)

Когда использовать: Маршрутизация событий по иерархическому ключу (order.created, order.payment.processed).

Fanout Exchange — broadcast (всем подписчикам)

Producer → Exchange: fanout → Queue A
                            → Queue B
                            → Queue C

Каждое сообщение → во ВСЕ привязанные очереди
Routing key игнорируется

Когда использовать: Pub-Sub: уведомления, события (все сервисы должны узнать о task.created).

Headers Exchange — маршрутизация по заголовкам (не routing key)

Producer → Exchange: headers → Queue A (x-match = any, header1 = "value1")
                             → Queue B (x-match = all, header1 = "value1", header2 = "value2")

Сообщение с headers: {header1: "value1", header3: "value3"} → Queue A (совпал 1 заголовок из 1)
Сообщение с headers: {header1: "value1", header2: "value2"} → Queue A + Queue B

Когда использовать: Сложная context-based маршрутизация (почти не используется на практике).

4.3. Таблица выбора Exchange

Exchange Type Маршрутизация по Пример binding Когда выбрать
Direct Точное совпадение routing key "error" → Queue A Распределение по типам/категориям
Topic Маска routing key (wildcard) "logs.#" → Queue A Иерархическая маршрутизация
Fanout Всем (broadcast) Широковещательные события
Headers По заголовкам (key=value) x-match=all Сложные условия (редко)

4.4. Dead Letter Queue (DLQ) — что должен знать аналитик

Зачем: Если consumer не может обработать сообщение (невалидные данные, ошибка внешней системы), RabbitMQ может отправить его в DLQ.

Как настроить:

Очередь: tasks.queue
  DLX: tasks.dlx (Dead Letter Exchange)
  DLQ: tasks.dlq (Dead Letter Queue)

Жизненный цикл сообщения:
  1. Producer → Exchange → tasks.queue
  2. Consumer пытается обработать → выбрасывает исключение
  3. Сообщение отклоняется (basic.nack) → попадает в tasks.dlx
  4. tasks.dlx → tasks.dlq
  5. Из DLQ можно переотправить (re-publish) или анализировать

Причины попадания в DLQ:

Причина Описание Действие аналитика
Consumer отклонил (nack) channel.basicNack(deliveryTag, false, false) Проверить, почему consumer не может обработать
Истёк TTL x-message-ttl превышен Увеличить TTL или сообщение испорчено
Очередь переполнена x-max-length превышен Увеличить лимит или распределить нагрузку
Binding не найден Нет очереди для routing key Проверить настройки exchange

Что делать с сообщениями в DLQ:

  1. Анализировать — понять причину (невалидные данные? баг?)
  2. Переотправить — исправить проблему и повторно опубликовать
  3. Автоматический retry — Consumer может проверить, не пора ли переотправить из DLQ
  4. Dead Letter Sink — сохранить в хранилище (S3, БД) для разбора

4.5. Publisher Confirms и Consumer ACK

Publisher Confirms — гарантия, что сообщение дошло до брокера:

Producer → RabbitMQ → Confirm (ACK from broker)

Producer знает: сообщение получено брокером (написано на диск)
Без confirms: producer не знает, дошло ли сообщение (может потеряться)

Consumer ACK — гарантия, что consumer обработал сообщение:

RabbitMQ → Consumer → ACK (сообщение удаляется из очереди)
           Consumer → NACK (сообщение → DLQ или возврат в очередь)
           Consumer → timeout без ACK (сообщение переотправляется другому consumer-у)

Режимы ACK:

Режим Когда ACK Риск Производительность
auto Автоматически при получении ❌ Потеря, если consumer упал до обработки 🚀 Высокая
manual Явный вызов basicAck() после обработки ✅ Нет потери 🐢 Средняя

Для аналитика: В требованиях обязательно указывайте manual ACK для критичных интеграций. auto ACK допустим только для логов и мониторинга.

4.6. Когда RabbitMQ — плохой выбор

Сценарий Почему RabbitMQ не подходит Что выбрать
Нужно хранить историю сообщений (аудит, replay) Сообщения удаляются после ACK Kafka
Пропускная способность > 100K msg/s RabbitMQ упирается в Erlang VM Kafka, Pulsar
Нужна строгая упорядоченность по ключу FIFO в одной очереди — да, но сложно с routing Kafka (partition по ключу)
Очень простое приложение (одна очередь) RabbitMQ — тяжеловат SQS (управляемый)
Сообщения > 100 MB (файлы, изображения) RabbitMQ не оптимизирован для больших сообщений S3 + ссылка в сообщении

5. Другие брокеры (обзорно)

5.1. AWS SQS + SNS

SQS (Simple Queue Service):

  • Управляемая очередь (без администрирования)
  • Модель: Queue (точка-точка)
  • Стандартная очередь: at-least-once, порядок не гарантирован
  • FIFO очередь: exactly-once, строгий порядок (но 3000 msg/s max)
  • Лучшее решение, когда не хотите администрировать брокер

SNS (Simple Notification Service):

  • Pub-Sub (Topic)
  • Подписчики: SQS, Lambda, Email, SMS, HTTP
  • Часто используется как SNS → SQS (Pub-Sub → Queue)

Паттерн SNS + SQS:

Producer → SNS Topic → SQS Queue A → Consumer A
                    → SQS Queue B → Consumer B
                    → SQS Queue C → Consumer C

Когда выбирать:

  • Вы уже в AWS
  • Не хотите администрировать Kafka/RabbitMQ
  • Нагрузка до десятков тысяч msg/s

5.2. NATS

  • Лёгкий, быстрый (10M msg/s на одном сервере)
  • Аналогия: UDP для сообщений
  • Модель: Pub-Sub, Queue Groups
  • Гарантии: at-most-once (по умолчанию)
  • НЕ хранит сообщения (если consumer отключён — потеря)
  • Используется в: IoT, real-time, микросервисы (NATS JetStream добавляет хранение)

5.3. Redis Pub-Sub / Redis Streams

Redis Pub-Sub:

  • Крайне быстрый (in-memory)
  • Не хранит сообщения — если consumer отключён в момент публикации, сообщение потеряно
  • Только at-most-once

Redis Streams (с 5.0):

  • Хранит сообщения (как Kafka, но in-memory)
  • Consumer Groups
  • Хорош для: кэш + очередь в одном (но не для тысяч сообщений)

5.4. Pulsar / Redpanda

Apache Pulsar:

  • Двухуровневая архитектура: BookKeeper (хранение) + Broker (обработка)
  • Separate storage and compute
  • Поддержка Geo-Replication из коробки

Redpanda:

  • Kafka API-совместимый (можно использовать Kafka-клиенты)
  • Быстрее Kafka (нет JVM, нет ZooKeeper)
  • Меньше задержка (p99)

5.5. Сводная таблица брокеров

Брокер Модель Хранение Порядок Произв. Сложность Управление
Kafka Log Диск (retention) Внутри партиции 🚀 Миллионы/сек 🔴 Высокая Self-managed / Cloud
RabbitMQ Queue Память/диск (до ACK) Внутри очереди 🟡 Десятки тыс/сек 🟡 Средняя Self-managed / Cloud
AWS SQS Queue AWS FIFO или нет 🟢 Сотни тыс/сек 🟢 Низкая Управляемый
AWS SNS Pub-Sub Нет Нет 🟢 Сотни тыс/сек 🟢 Низкая Управляемый
NATS Pub-Sub Нет (есть JetStream) Нет 🚀 Миллионы/сек 🟢 Низкая Self-managed
Redis Pub-Sub / Stream Память (опционально) Внутри потока 🚀 In-memory 🟢 Низкая Self-managed / Cloud
Pulsar Log Диск (BookKeeper) Внутри партиции 🚀 Миллионы/сек 🔴 Высокая Self-managed / Cloud
Redpanda Log Диск (без JVM) Внутри партиции 🚀 Миллионы/сек 🟡 Средняя Self-managed / Cloud

6. Как аналитику выбирать брокера

6.1. Алгоритм выбора

┌──────────────────────────────────────────────────────────────────┐
│ 1. Нужна ли история сообщений (replay, аудит)?                   │
│    Да ──→ Нужен ли cloud?                                       │
│            Да ──→ Confluent Kafka / AWS MSK / Redpanda Cloud     │
│            Нет ──→ Kafka / Pulsar / Redpanda                     │
│    │                                                             │
│    Нет ─→ 2. Нужна сложная маршрутизация?                       │
│            Да ──→ RabbitMQ (Exchange + routing key)              │
│            │                                                     │
│            Нет ─→ 3. Нужно минимум операций (управляемый)?       │
│                    Да ──→ AWS SQS (+ SNS для pub-sub)            │
│                    Нет ──→ RabbitMQ                              │
│                                                                    │
│ Дополнительные фильтры:                                          │
│   - Пропускная способность < 10K msg/s → любой                    │
│   - Пропускная способность > 100K msg/s → Kafka / Pulsar         │
│   - Строгий порядок по сущности → Kafka (partition key)          │
│   - Отложенные задачи (delay) → RabbitMQ (TTL) / SQS (delay)     │
│   - Уже используете AWS/GCP/Azure → их сервисы                   │
└──────────────────────────────────────────────────────────────────┘

6.2. Что должно быть в требованиях к брокеру

Параметр Пример требования Почему важно
Модель Queue (точка-точка) или Pub-Sub Определяет, сколько consumer-ов получат сообщение
Гарантии доставки At-least-once Определяет, могут ли быть дубликаты
Порядок сообщений По ключу orderId внутри партиции Определяет бизнес-корректность
Retention 7 дней Как долго можно перечитывать
Пропускная способность 500 msg/s average, 2000 msg/s peak Определяет выбор брокера
Размер сообщения Средний 5 KB, макс 50 KB Определяет, не нужен ли S3 для больших файлов
Replay Возможность перечитать сообщения за последние 7 дней Нужно ли для аудита / восстановления
DLQ Необработанные сообщения — в DLQ, алерт при > 10 в DLQ Обработка ошибок
Мониторинг Consumer lag < 1000 сообщений Обнаружение проблем
Managed / Self Managed (AWS MSK) Кто администрирует

6.3. Практический пример выбора

Кейс: Интернет-магазин

Интеграция Брокер Почему
Уведомление о новом заказе (email + SMS) RabbitMQ Точка-точка (один email, одно SMS). Нужна маршрутизация (email → Email Queue, SMS → SMS Queue)
События заказа (created, paid, shipped) Kafka Pub-Sub: аналитика, аудит, склад, уведомления. Нужна история для replay. Важен порядок по orderId
Обработка изображений товаров SQS Простая очередь задач. Нет порядка. Нужен managed (не хотим админить)
Метрики и мониторинг Kafka Высокая пропускная способность (миллионы событий). At-most-once (потеря метрики не страшна)

7. Dead Letter Queue (DLQ) — проектирование для аналитика

7.1. Общая схема DLQ

                    ┌──────────┐
                    │ Producer │
                    └────┬─────┘
                         │
                         ▼
                 ┌───────────────┐
                 │ Main Queue    │
                 │ (tasks.queue) │
                 └───────┬───────┘
                         │
              ┌──────────┴──────────┐
              │                     │
              ▼                     ▼
      ┌──────────────┐    ┌──────────────┐
      │ Consumer OK  │    │ Consumer     │
      │ → ACK        │    │ FAIL        │
      │ → удаление   │    │ → NACK      │
      └──────────────┘    └──────┬───────┘
                                 │
                                 ▼
                        ┌───────────────┐
                        │ DLQ           │
                        │ (tasks.dlq)   │
                        └───────┬───────┘
                                │
                    ┌───────────┴───────────┐
                    │                       │
                    ▼                       ▼
            ┌──────────────┐       ┌──────────────┐
            │ Ручной разбор │       │ Автоматический│
            │ (Dashboard)   │       │ retry через N│
            └──────────────┘       │ минут        │
                                    └──────────────┘

7.2. Параметры DLQ в RabbitMQ

Параметр Описание Пример
x-dead-letter-exchange Exchange, куда отправлять «мёртвые» сообщения tasks.dlx
x-dead-letter-routing-key Routing key для DLQ tasks.dlq
x-message-ttl Время жизни сообщения в основной очереди 60000 (1 минута)
x-max-retries Количество попыток перед DLQ (через DLQ + republish) 3

7.3. Шаблон «Retry with DLQ» для аналитика

Требования к DLQ (Retry Policy):

1. Consumer пытается обработать сообщение
2. Если ошибка временная (timeout, 503)  
   отклонить с requeue=false  сообщение в DLQ
3. Из DLQ сообщение возвращается в основную очередь через 60 секунд
4. Максимум 3 попытки (retry count в заголовке сообщения)
5. После 3 неудач  сообщение остаётся в DLQ для ручного разбора
6. Алерт: если в DLQ больше 10 сообщений  уведомление дежурному

7.4. Poison Message — что делать

Poison Message — сообщение, которое consumer не может обработать никогда (невалидный JSON, отсутствующий ID сущности).

Сценарий без DLQ:

Consumer → NACK → возврат в очередь → снова NACK → бесконечный цикл

Сценарий с DLQ:

Consumer → NACK → в DLQ → алерт → дата-аналитик исправляет данные → 
переотправка из DLQ → success

Как аналитик должен описать обработку Poison Messages:

Req-INT-005: Обработка Poison Messages
Если consumer не может обработать сообщение после 3 попыток:
  1. Сообщение помещается в DLQ
  2. Генерируется алерт в мониторинг (Slack / PagerDuty)
  3. Сообщение хранится в DLQ 30 дней
  4. Дата-аналитик вручную анализирует и исправляет данные
  5. После исправления — сообщение переотправляется в основную очередь

8. Типовые проблемы с брокерами и их диагностика

8.1. Consumer Lag

Проблема: Consumer отстаёт от Producer. Сообщения накапливаются.

Producer пишет: 100 msg/s
Consumer читает: 10 msg/s
Lag: 90 msg/s → очередь растёт → задержка обработки (latency растёт)

Причины:

  • Consumer слишком медленный (N+1 запросы, блокировки)
  • Слишком мало consumer-ов в группе
  • Слишком мало партиций (для Kafka)
  • Внешняя система (API, БД) тормозит

Что проверять аналитику:

  • Kafka: kafka-consumer-groups --group order-service --describe → LAG
  • RabbitMQ: Management UI → Queues → Ready + Unacknowledged
  • SQS: CloudWatch → ApproximateNumberOfMessagesVisible

Когда это критично:

  • Уведомления в реальном времени (Lag > 1 минуты — неприемлемо)
  • Платежи (Lag > несколько секунд — потеря клиентов)
  • Некритично: аналитика, отчёты (Lag может быть часами)

8.2. Consumer Rebalancing

Проблема: В Kafka при изменении состава Consumer Group (новый consumer, падение, остановка) происходит rebalance — перераспределение партиций.

До rebalance:
  Consumer A: Partition 0, Partition 1
  Consumer B: Partition 2, Partition 3

Consumer A упал → REBALANCE → все consumer-ы останавливаются на несколько секунд

После rebalance:
  Consumer B: Partition 0, Partition 1, Partition 2, Partition 3 (все партиции)

Последствия:

  • Все consumer-ы в группе останавливаются на время rebalance
  • Сообщения не обрабатываются в этот период
  • После rebalance — Consumer B читает всё с начала (или с последнего commit offset)

Для аналитика: Частые rebalance — симптом нестабильности. В требованиях укажите session.timeout.ms и heartbeat.interval.ms.

8.3. Потеря сообщений — причины и предотвращение

Причина Где Как предотвратить
Producer не получил ACK → думает, что отправил Kafka: acks=0 Использовать acks=all
Consumer auto-ACK до обработки RabbitMQ: auto ACK Использовать manual ACK
Сообщение удалено по retention до обработки Kafka Увеличить retention, мониторить lag
Rebalance при несохранённом offset Kafka Сохранять offset после обработки
Exchange не нашёл очередь RabbitMQ Настроить Alternate Exchange или Publisher Confirm
SQS — Standard queue (дубликаты) SQS Идемпотентность на стороне consumer
Сетевая потеря при publish Любой Retry + idempotency key

9. Моделирование брокеров в BPMN и Sequence Diagrams

9.1. Sequence Diagram с Kafka

@startuml
actor Клиент as Client
participant "Order Service" as OrderService
collections "Kafka: order.events" as Kafka #LightBlue
participant "Payment Service" as PaymentService
participant "Notification Service" as NotificationService

Client -> OrderService: POST /orders (создать заказ)
activate OrderService

OrderService -> OrderService: Создать заказ в БД
OrderService -> Kafka: publish(order.created, key=orderId)
note right: partition = hash(orderId) % N

deactivate OrderService

== Асинхронная обработка ==

Kafka -> PaymentService: consume(order.created)
activate PaymentService
PaymentService -> PaymentService: Списать средства
PaymentService -> Kafka: publish(payment.processed, key=orderId)
deactivate PaymentService

Kafka -> NotificationService: consume(payment.processed)
activate NotificationService
NotificationService -> Client: Email: "Заказ оплачен"
deactivate NotificationService
@enduml

9.2. Sequence Diagram с RabbitMQ

@startuml
actor "Менеджер" as Manager
participant "CRM" as CRM
collections "RabbitMQ: Exchange" as RMQ #LightBlue
queue "Email Queue" as EmailQueue
queue "SMS Queue" as SMSQueue
participant "Email Worker" as EmailWorker
participant "SMS Worker" as SMSWorker

Manager -> CRM: Создать клиента
activate CRM

CRM -> CRM: Сохранить в БД
CRM -> RMQ: publish(routingKey="notification.welcome")
note right: Topic Exchange: notification.#

deactivate CRM

RMQ -> EmailQueue: binding: notification.#
RMQ -> SMSQueue: binding: notification.#

EmailQueue -> EmailWorker: deliver
activate EmailWorker
EmailWorker -> Manager: Email: Добро пожаловать!
deactivate EmailWorker

SMSQueue -> SMSWorker: deliver
activate SMSWorker
SMSWorker -> Manager: SMS: Добро пожаловать!
deactivate SMSWorker
@enduml

9.3. BPMN с брокером

[Получен заказ] → [Создать заказ] → [Отправить событие в Kafka]
                                        │
                                        │ topic: order.events
                                        ▼
                              [Параллельный шлюз]
                              ┌────────┴────────┐
                              │                  │
                        [Списать средства]  [Отправить уведомление]
                              │                  │
                              ▼                  ▼
                        [Заказ оплачен]    [Уведомление отправлено]
                              │                  │
                              └────────┬─────────┘
                                       ▼
                                 [Подтвердить заказ]

Задание для аналитика: Нарисовать BPMN-процесс с асинхронными шагами через брокер, указав топики/очереди на соединительных линиях.


10. Чек-лист аналитика при проектировании с брокером

Проверка Где
1 Выбрана модель: Queue или Log-based?
2 Выбран брокер под задачу (Kafka/RabbitMQ/SQS)?
3 Kafka: определён ключ партиционирования (по сущности)?
4 Kafka: определено количество партиций (≥ кол-ва consumer-ов)?
5 Kafka: retention (сколько дней хранить)?
6 Kafka: acks (0/1/all) для Producer?
7 RabbitMQ: выбран тип Exchange (Direct/Topic/Fanout)?
8 RabbitMQ: настроен binding (routing key)?
9 RabbitMQ: manual ACK для критичных операций?
10 Гарантии доставки: At-least-once / At-most-once?
11 Идемпотентность: consumer dedup по messageId / eventId?
12 DLQ: настроена Dead Letter Queue?
13 DLQ: сколько retry-попыток, TTL?
14 DLQ: алерт при N сообщений в DLQ?
15 Poison messages: что делать с необрабатываемыми?
16 Мониторинг: consumer lag, алерты?
17 Размер сообщения: не превышает лимиты брокера?
18 Security: шифрование в transit (TLS) + auth?
19 Schema: формат сообщения описан (AVRO, JSON Schema)?
20 Versioning: schema evolution (как менять формат)?

Вопросы для самопроверки

Базовый уровень

  1. Зачем нужен брокер сообщений? Какие проблемы он решает по сравнению с прямой HTTP-интеграцией?
  2. В чём фундаментальная разница между Queue (RabbitMQ) и Log-based (Kafka)? Какая модель позволяет перечитывать историю?
  3. Что такое partition в Kafka? Почему ключ партиционирования важен для порядка сообщений?
  4. Какие четыре типа Exchange есть в RabbitMQ? Какой тип используется для broadcast (всем подписчикам)?
  5. Что такое Dead Letter Queue (DLQ)? Зачем она нужна?

Продвинутый уровень

  1. Kafka partition count: Аналитик спроектировал топик с 1 партицией и 10 consumer-ами. В чём проблема? Сколько партиций нужно?
  2. Order guarantee: Почему randomUUID в качестве ключа партиционирования — фатальная ошибка для бизнеса, требующего порядка?
  3. Replay: Вам нужно восстановить состояние системы на момент 3 дня назад, потому что баг испортил данные. Какой брокер (Kafka или RabbitMQ) позволит это сделать? Как?
  4. DLQ проектирование: Consumer падает с ошибкой при обработке сообщения. Опишите пошагово жизненный цикл сообщения: от публикации до DLQ. Какие конфигурации RabbitMQ для этого нужны?
  5. Выбор брокера: Спортивная ставка. Вам нужно: 1) высокая пропускная способность (>100K msg/s матчей), 2) порядок по matchId, 3) хранение 30 дней для аудита, 4) at-least-once. Какой брокер выберете?

Практическое задание

Задание 1. Выбор модели и брокера (2 балла)

Для каждого сценария выберите модель (Queue / Log-based) и конкретного брокера (Kafka / RabbitMQ / SQS). Обоснуйте.

Сценарий Модель Брокер Обоснование
1 Отправка приветственного email после регистрации
2 События изменения статуса заказа для аналитики и аудита
3 Обработка загруженных изображений (ресайз, watermark)
4 Поток котировок акций (1000 обновлений/сек)
5 Уведомление о новой задаче в Telegram + Slack + Email

Задание 2. Проектирование Kafka-топика (3 балла)

Вы проектируете систему доставки еды. При создании заказа нужно:

  1. Уведомить ресторан (новый заказ)
  2. Найти курьера
  3. Списать средства с карты клиента
  4. Отправить подтверждение клиенту (email + push)
  5. Записать событие в аудит

Требуется: 2.1. (1 балл) Спроектировать Kafka-топик: название, количество партиций, ключ партиционирования, retention. 2.2. (1 балл) Определить Consumer Group для каждого сервиса. Сколько consumer-ов в каждой группе? 2.3. (1 балл) Какие гарантии доставки (acks) для каждого типа сообщений?

Задание 3. RabbitMQ Exchange + Binding (2 балла)

Система мониторинга отправляет события с routing key:

  • monitoring.cpu.high
  • monitoring.memory.critical
  • monitoring.disk.warning
  • monitoring.disk.critical
  • monitoring.network.latency.high

Нужно:

  • Queue A: все события с уровнем "critical" (любая категория)
  • Queue B: все события с категорией "disk" (любой уровень)
  • Queue C: все события "monitoring.network.latency.high" (точно)

3.1. (1 балл) Какой тип Exchange выбрать и какие binding настроить? 3.2. (1 балл) В Queue A пришло сообщение, которое consumer не смог обработать. Опишите конфигурацию DLQ для Queue A (названия exchange, routing key, количество retry).

Задание 4. Диагностика проблемы (2 балла)

Кейс: В системе бронирования отелей используется Kafka. Топик booking.events (3 партиции) с ключом bookingId. Consumer Group notification-service (3 инстанса).

В один момент перестали приходить уведомления о подтверждении бронирований. При проверке:

kafka-consumer-groups --group notification-service --describe

GROUP                TOPIC           PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG
notification-service booking.events  0          1500            1520             20
notification-service booking.events  1           500             510             10
notification-service booking.events  2             0              10             10

4.1. (1 балл) Проанализируйте вывод. Какая партиция вызывает беспокойство? Почему? 4.2. (0,5 балла) Какие возможные причины проблемы с этой партицией? 4.3. (0,5 балла) Что вы сделаете как аналитик: какие вопросы зададите разработчикам/архитектору?

Задание 5. DLQ-стратегия (1 балл)

Спроектируйте DLQ-стратегию для сервиса уведомлений (email + SMS):

  • При регистрации пользователя отправляется приветственное письмо
  • Email-сервис (consumer) вызывает внешний SMTP-сервер, который иногда отвечает 5xx (временная ошибка)
  • При невалидном email (формат abc@def) — письмо отправить нельзя никогда

Опишите:

  • Какой брокер и почему
  • Параметры DLQ (количество retry, TTL, алерт)
  • Что делать с невалидными email (poison messages)
  • Как обрабатывать временные ошибки SMTP

Критерии оценки

Задание Баллы
Задание 1: Выбор модели и брокера 2
Задание 2: Проектирование Kafka-топика 3
Задание 3: RabbitMQ Exchange + Binding 2
Задание 4: Диагностика проблемы 2
Задание 5: DLQ-стратегия 1
Итого 10

Справочные материалы

Kafka: ключевые параметры для аналитика

Параметр Описание Типичное значение
num.partitions Количество партиций в топике 3-12 (зависит от нагрузки)
retention.ms Время хранения сообщений 604800000 (7 дней)
retention.bytes Максимальный размер на партицию -1 (без ограничения)
cleanup.policy delete / compact delete (для событий)
acks Подтверждение записи all (для критичных)
min.insync.replicas Минимум in-sync реплик 2

RabbitMQ: ключевые параметры для аналитика

Параметр Описание Типичное значение
x-message-ttl TTL сообщения в очереди 60000 (60 сек)
x-max-length Максимум сообщений в очереди 10000
x-dead-letter-exchange DLX для необработанных tasks.dlx
x-dead-letter-routing-key Routing key для DLQ tasks.dlq
x-max-priority Приоритеты сообщений 10

Инструменты

Инструмент Назначение
Kafka CLI (kafka-topics, kafka-console-producer, kafka-console-consumer, kafka-consumer-groups) Управление топиками, просмотр сообщений, мониторинг lag
Kafka UI (AKHQ, Kafdrop, Confluent Control Center) Web-интерфейс для Kafka
RabbitMQ Management UI Web-интерфейс (очереди, exchange, binding, мониторинг)
SQS Console (AWS) Управление очередями, просмотр сообщений
Offset Explorer (бывший Kafka Tool) Десктопный клиент для Kafka

📚 Материалы модуля

🖼️ Схема и инфографика

🎬 Видео-лекция

🎬 API и интеграции

📄 Дополнительные материалы (PDF)

📄API Integration Blueprints
Скачать
Спросить ИИ