Москва: проверенные б/у ноутбуки с гарантией и проверкой перед оплатой
+7 906 722-94-45

Главная → Статьи → Kafka: основы для разработчика

Apache Kafka глазами разработчика: топики, партиции, консьюмеры

Модель Kafka без магии: топики и партиции как журнал, продюсеры с ключами, consumer groups и офсеты, гарантии доставки и когда Kafka нужна, а когда избыточна.

Прямой ответ

Kafka — не «очередь побольше», а распределённый журнал записи: топики поделены на партиции (неупорядоченные сами по себе, но строго упорядоченные внутри партиции append-only логи), продюсеры дописывают сообщения, consumer groups читают независимо друг от друга, каждая группа ведёт свои офсеты (позицию чтения) — отсюда главное отличие от RabbitMQ-подобных очередей: сообщение не удаляется после прочтения (хранится по retention — днями/неделями), и его могут перечитывать (восстановление, новый сервис, отладка — перемотка офсета). Три понятия, на которых стоит практика: ключ сообщения определяет партицию (хеш ключа — все события одного заказа в одной партиции = порядок для заказа гарантирован; пустой ключ — round-robin по партициям); consumer group — единица масштабирования и балансировки (внутри группы партиция читается ровно одним консьюмером: консьюмеров больше партиций — лишние простаивают; несколько групп читают один топик независимо — паттерн «одни данные, много обработчиков»); офсет коммитится консьюмером — до обработки (at-most-once, потеря при падении) или после (at-least-once, дубли при падении между обработкой и коммитом — те же гарантии, что у любых очередей: идемпотентная обработка обязательна). Когда Kafka нужна: потоки событий с высокой пропускной способностью, несколько независимых подписчиков, replay/аудит, стриминг; когда избыточна: «Celery-задачи для отправки писем» — классическая очередь проще и дешевле.

Топики, партиции, продюсеры

Топик — логический поток (orders.events), физически — N партиций-журналов (типично 6–24 на старте; партиции — единица параллелизма и репликации). Продюсер: acks — гарантия записи (acks=1 — лидер записал; acks=all — все in-sync реплики (дольше, но переживает падение брокера); acks=0 — «бросил»), enable.idempotence=true — дедупликация на брокере (сетевые ретраи продюсера не дают дублей — включать всегда, это почти бесплатно), батчинг и linger.ms — пропускная способность ценой миллисекунд задержки. Ключ партиционирования — архитектурное решение: order_id даёт порядок событий заказа и локальность обработки; «горячий ключ» (крупный клиент) перегружает одну партицию — перекос нагрузки, который нельзя исправить добавлением консьюмеров (партиция неделима между ними); изменение числа партиций топика ломает соответствие ключ→партиция для старых данных (порядок по ключу между старым и новым раскладом не сохраняется — планировать количество заранее). Схемы сообщений — контракт: Avro/Protobuf со Schema Registry (эволюция схемы контролируема) или JSON с версией в payload + tolerant reader (вебхук-дисциплина применима целиком).

Consumer groups, офсеты и rebalance

Группа читает топик так, что каждая партиция закреплена за одним консьюмером группы: 6 партиций / 3 консьюмера = по 2 каждому; консьюмер падает — rebalance перераспределяет партиции (на время — пауза чтения; частые ребалансы — симптом: max.poll.interval.ms превышается долгой обработкой батча → брокер считает консьюмер мёртвым — уменьшать батч/выносить обработку в отдельный пул). Офсеты хранятся в системном топике группы: commit после обработки — стандарт (повтор при падении — идемпотентность закрывает); auto.offset.reset — поведение новой группы (earliest — прочитать всю историю (Kafka позволяет!), latest — только новое). Независимость групп — киллер-фича: аналитический сервис добавляется новой группой и читает топик с начала, не мешая основному обработчику (в очередной модели для этого пришлось бы дублировать сообщения — сравнение с классическими очередями). Порядок: гарантирован внутри партиции — обработчик, которому важен порядок сущности, должен читать синхронно по партиции (параллельная обработка батча внутри партиции порядок теряет — воркер-пул с группировкой по ключу, если нужно и то и другое). Dead-letter — паттерн приложения: ядовитое сообщение после N попыток пишется в topic.DLQ (штатной DLQ нет — своя дисциплина).

Гарантии, retention, эксплуатация

Доставка end-to-end: продюсер acks=all + enable.idempotence + консьюмер «commit после обработки» + идемпотентный обработчик = effectively-once эффект (формально at-le-once доставка, exactly-once семантика эффекта — разбор почему так); транзакции Kafka (exact-once между топиками) — для стрим-процессинга (Kafka Streams), прикладному коду обычно избыточны. Retention — хранение по времени/размеру (retention.ms=7d типично): журнал чистится независимо от прочтения (компактные топики cleanup.policy=compact — отдельный режим «последнее значение по ключу» — состояние/справочники). Эксплуатационный минимум: мониторинг consumer lag (отставание группы в сообщениях — главная метрика здоровья: растёт — обработчики не успевают — алерты), время rebalance, ISR-состав (реплики); Kafka в облаке (MSK, Confluent) снимает половину забот (брокеры, репликация, обновления) — для команды без выделенной инфраструктуры разумный старт; self-hosted кластер — заметная статья эксплуатации (ZooKeeper-эпоха ушла, KRaft упростил, но брокеры/диски/балансировка остаются).

Практический сценарий: события заказов для трёх подписчиков

Задача: сервис заказов публикует события (created/paid/shipped); подписчики — склад, уведомления, аналитика. Дизайн: топик orders.events, 12 партиций, ключ — order_id (порядок событий заказа), Avro-схемы в Schema Registry (эволюция полей без поломок — аналитика и склад на разных версиях схемы сосуществуют); продюсер: acks=all, идемпотентность вкл; обработчики — три consumer group (warehouse, notify, analytics) — полностью независимы: уведомления отстают на пике — склад не замечает. Обработчик уведомлений: батч 100, commit после отправки (at-le-once) + идемпотентность по event_id в Redis (кеш) — повтор после ребаланса не шлёт второе письмо; ядовитые события (битая схема после бага) — после 3 попыток в orders.events.DLQ + алерт. Аналитика добавлена через месяц — новая группа с auto.offset.reset=earliest прочитала всю историю с начала топика (retention 30 дней) — backfill без выгрузки из БД. Инцидент-учение: kill -9 обработчика посреди батча — после рестарта 40 сообщений перечитаны, письма не задвоены (идемпотентность подтверждена); lag-метрика на алерте — «обработчик встал» видно за минуту, а не по жалобам. Сценарий показывает нишу Kafka: три независимых потребителя + история + порядок по сущности — классической очередью это эмулировалось бы костылями.

FAQ

Kafka против RabbitMQ — что выбрать? Модели разные: RabbitMQ — «умный брокер, глупый консьюмер» (гибкая маршрутизация, удаление после ack, приоритеты — задачи/команды); Kafka — «глупый брокер, умный консьюмер» (журнал, replay, много групп, партиционный параллелизм — потоки событий); «нужна просто очередь задач» — RabbitMQ/Redis (Celery); «событийный поток с историей и подписчиками» — Kafka.

Сколько партиций делать? От целевой пропускной способности и параллелизма консьюмеров (партиций ≥ максимума консьюмеров группы); закладывать рост (добавить партиции можно, убрать — нет; но и 500 партиций на топик — нагрузка на метаданные/ребалансы); стартовые 12–24 — разумный дефолт для продуктовых потоков.

Правда ли, что Kafka не теряет сообщения? Только при правильных настройках: acks=all + min.insync.replicas=2 + репликация ≥3 (иначе падение лидера с единственным ack — потеря); дефолты разных стендов различаются — «Kafka надёжна» верно для настроенной Kafka.

Нужен ли ZooKeeper? Больше нет: KRaft-режим (Kafka 3.x) держит метаданные силами самих брокеров — новые кластеры ставятся без ZK; встречающиеся еще ZK-кластеры — наследие, мигрируемое при обновлениях.

Источники: документация Apache Kafka (semantics), Confluent-курсы — ссылки в шапке.