Брокеры сообщений и Apache Kafka: Архитектура, Гарантии и Паттерны

Оглавление

  1. Синхронное и асинхронное взаимодействие
  2. Основы Apache Kafka
  3. Архитектура хранения: Партиции и Сегменты
  4. Публикация сообщений (Producer)
  5. Чтение сообщений (Consumer) и Ребалансировка
  6. Семантики доставки сообщений
  7. Репликация и отказоустойчивость
  8. Оптимизация: Пакетная отправка и Сжатие
  9. Обработка ошибок: Retry и DLQ
  10. Продвинутые паттерны (Outbox, Inbox, Saga)
  11. Мониторинг и метрики

1. Синхронное и асинхронное взаимодействие

Основные модели

  • Синхронное (Блокирующее): Вызывающая сторона ожидает завершения операции.
    • Недостатки: Рост задержек (latency), эффект домино при деградации сервисов, сильная связанность (Tight Coupling).
  • Асинхронное (Неблокирующее): Инициирование операции и мгновенное продолжение работы. Реализуется через очереди.

Роль брокера сообщений

Брокер (например, Kafka) разрывает прямую связь между сервисами:

  • Продюсер (Producer): Отправляет сообщение в очередь.
  • Консьюмер (Consumer): Забирает сообщение из очереди для обработки.
  • Принцип: FIFO (First-In, First-Out).

2. Основы Apache Kafka

Kafka — это распределенная, отказоустойчивая платформа для потоковой обработки событий.

Основные компоненты

  • Broker (Брокер): Отдельный сервер Kafka.
  • Cluster (Кластер): Группа брокеров, работающих совместно.
  • Topic (Топик): Именованная категория сообщений (аналог таблицы в БД).
  • Partition (Партиция): Часть топика, представляющая собой упорядоченный, неизменяемый лог сообщений.
  • Zookeeper / KRaft: Сервисы для хранения метаданных кластера.

Особенности Kafka

  • Высокая пропускная способность: Миллионы сообщений в секунду.
  • Сохранение данных: Сообщения не удаляются сразу после прочтения (в отличие от RabbitMQ).
  • Перечитывание: Возможность возвращаться к старым сообщениям по их смещению (Offset).

3. Архитектура хранения: Партиции и Сегменты

Хранение на диске

  • Партиция физически состоит из файлов-сегментов.
  • Retention Policy (Политика хранения):
    • retention.ms: Ограничение по времени (по умолчанию 7 дней).
    • retention.bytes: Ограничение по размеру партиции.
  • Cleanup Policy (Политика очистки):
    • delete: Удаление старых сегментов целиком (FIFO).
    • compact: Сжатие лога. Kafka оставляет только последнее сообщение для каждого уникального ключа.

4. Публикация сообщений (Producer)

Структура сообщения

  • Key (Ключ): Используется для партиционирования.
  • Value (Значение): Полезная нагрузка (массив байт).
  • Headers: Метаданные.

Стратегии партиционирования

Определяют, в какую партицию попадет сообщение:

  • Manual: Продюсер явно указывает номер партиции.
  • Random: Случайный выбор (подходит, если порядок не важен).
  • Hash Partitioner: hash(key) % num_partitions. Гарантирует, что все сообщения с одним ключом попадут в одну партицию (сохранение порядка для конкретной сущности).

Параметр acks (Подтверждения)

  • acks=0: Без подтверждения. Максимальная скорость, высокий риск потери.
  • acks=1: Ждем только Лидера. Баланс скорости и надежности.
  • acks=-1 (или all): Ждем Лидера и все синхронизированные реплики (ISR). Максимальная гарантия (ACID-подобная надежность доставки).

5. Чтение сообщений (Consumer) и Ребалансировка

Consumer Group (Группа консьюмеров)

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

Offset (Смещение)

  • Уникальный порядковый номер сообщения в партиции.
  • Commit Offset: Процесс записи прогресса чтения в служебный топик __consumer_offsets. Чтение — это двухэтапная операция: Чтение + Запись.

Ребалансировка (Rebalancing)

Процесс перераспределения партиций при изменении состава группы.

  • Проблема Stop-the-World: Все консьюмеры группы останавливают чтение на время процесса.
  • Триггеры: session.timeout.ms, max.poll.interval.ms.
  • Лучшая практика: Изоляция потоков (один топик — одна группа), чтобы ошибка в одном потоке не останавливала чтение других.

6. Семантики доставки сообщений

Определяют, сколько раз сообщение будет обработано.

  • At most once (Не более одного раза):
    • Возможна потеря, дублей нет.
    • acks=0 или коммит смещения до обработки.
  • At least once (Хотя бы один раз):
    • Потеря исключена, возможны дубли.
    • acks=all, повторы (retries > 0), коммит смещения после успешной обработки.
    • Требует идемпотентности на стороне потребителя.
  • Exactly once (Ровно один раз):
    • Самый строгий уровень.
    • Использование идемпотентного продюсера (enable.idempotence=true) и транзакционной записи (БД + Kafka в одной транзакции).

7. Репликация и отказоустойчивость

Роли реплик

  • Leader (Лидер): Обрабатывает все запросы записи и чтения.
  • Follower (Последователь): Копирует данные с лидера.
  • ISR (In-Sync Replicas): Список реплик, которые не отстают от лидера.

Управление надежностью

  • min.insync.replicas: Минимальное кол-во реплик, которые должны подтвердить запись при acks=all.

8. Оптимизация: Пакетная отправка и Сжатие

Пакетная отправка (Batching)

Каждое сообщение имеет оверхед 61 байт.

  • linger.ms: Время накопления пакета.
  • batch.size: Максимальный размер пакета в байтах.

Сжатие (Compression)

  • На стороне Продюсера: Снижает нагрузку на сеть. Рекомендуется (Gzip, Snappy, LZ4, Zstd).
  • На стороне Брокера: Экономит только место на диске, не снижая сетевой трафик от продюсера.

9. Обработка ошибок: Retry и DLQ

Ошибки делятся на Исправимые (временные сбои) и Неисправимые (битая схема данных).

Стратегии

  1. Retry Queue (Очередь повторов): Сообщение перекладывается в retry-топик для повторной попытки через время.
  2. DLQ (Dead Letter Queue — «Кладбище»): Сообщения, которые не удалось обработать после всех попыток, уходят в ERROR-топик для ручного разбора.
  3. Сохранение порядка: При ретрае одного сообщения рекомендуется отправлять в ретрай и все последующие сообщения по этой же сущности, чтобы не нарушить последовательность.

10. Продвинутые паттерны (Outbox, Inbox, Saga)

Эти паттерны решают проблему атомарности изменений в распределенных системах (аналог ACID-транзакций).

Transactional Outbox

  • Проблема: Как одновременно обновить БД и отправить сообщение в Kafka?
  • Решение: В рамках одной локальной транзакции БД обновляем бизнес-таблицу и пишем сообщение в таблицу outbox. Отдельный процесс-релейер (Relayer) читает outbox и шлет в Kafka.

Inbox Pattern

  • Проблема: Долгая обработка на стороне консьюмера приводит к ребалансировкам.
  • Решение: Консьюмер быстро сохраняет сообщение в локальную таблицу inbox и коммитит оффсет. Фоновый воркер читает из inbox и выполняет тяжелую логику. Обеспечивает дедупликацию.

Saga

Управление транзакциями через множество микросервисов.

  • Хореография: Сервисы общаются событиями. Высокая производительность, сложность отладки.
  • Оркестровка: Центральный сервис управляет шагами. Проще контроль, риск единой точки отказа.
  • Компенсирующие транзакции: Обязательный механизм “отката” (например, отмена бронирования), если один из шагов саги упал.

11. Мониторинг и метрики

  • Consumer Lag (Задержка): Разница между последним сообщением в Kafka и последним обработанным сообщением консьюмера. Самая важная метрика.
  • Throughput (Пропускная способность): Байт/сек или сообщений/сек.
  • Broker Resources: CPU, RAM, Disk I/O.
  • Latency (Задержка): Время жизни сообщения от отправки до обработки.

Итог: Kafka — мощный инструмент для асинхронности. Главный секрет успеха — баланс между надежностью (acks, транзакции) и производительностью (батчинг, сжатие), а также обязательная готовность к ошибкам и ребалансировкам.