Брокеры сообщений и Apache Kafka: Архитектура, Гарантии и Паттерны
Оглавление
- Синхронное и асинхронное взаимодействие
- Основы Apache Kafka
- Архитектура хранения: Партиции и Сегменты
- Публикация сообщений (Producer)
- Чтение сообщений (Consumer) и Ребалансировка
- Семантики доставки сообщений
- Репликация и отказоустойчивость
- Оптимизация: Пакетная отправка и Сжатие
- Обработка ошибок: Retry и DLQ
- Продвинутые паттерны (Outbox, Inbox, Saga)
- Мониторинг и метрики
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
Ошибки делятся на Исправимые (временные сбои) и Неисправимые (битая схема данных).
Стратегии
- Retry Queue (Очередь повторов): Сообщение перекладывается в
retry-топик для повторной попытки через время. - DLQ (Dead Letter Queue — «Кладбище»): Сообщения, которые не удалось обработать после всех попыток, уходят в
ERROR-топик для ручного разбора. - Сохранение порядка: При ретрае одного сообщения рекомендуется отправлять в ретрай и все последующие сообщения по этой же сущности, чтобы не нарушить последовательность.
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, транзакции) и производительностью (батчинг, сжатие), а также обязательная готовность к ошибкам и ребалансировкам.