Big Data

Kafka: архитектура и паттерны

LinkedIn в 2011 году опубликовал Kafka с описанием задачи: перекачать активность 175 миллионов пользователей через десятки сервисов без потерь и дублей. Сегодня Kafka обрабатывает более 7 триллионов сообщений в сутки в одном только LinkedIn. В Uber, Netflix и Airbnb Kafka стал нервной системой всей real-time инфраструктуры. За этой масштабируемостью стоят четыре простых концепции: topics, partitions, consumer groups и delivery semantics.

  • **LinkedIn** использует Kafka для activity stream: 7+ триллионов сообщений/сутки, единый поток данных между 1000+ микросервисов
  • **Uber** строит real-time карту спроса через Kafka: GPS-события -> partition по геохешу -> аналитика без задержки
  • **Confluent Cloud** управляет миллионами topic-ов для enterprise клиентов: Schema Registry + Kafka Connect превращают Kafka в центр интеграции данных

Topics и Partitions

LinkedIn в 2010 году столкнулся с проблемой: 300 сервисов хотят получать события действий пользователей. Прямые HTTP-вызовы между сервисами создают n² связей, система падает при любой перегрузке. Решение - **распределённый лог событий**. Продюсер пишет в один конец, консьюмеры читают независимо со своей скоростью. Так появился Kafka.

**Topic** - именованный поток сообщений, аналог таблицы в БД. Сообщения в topic-е хранятся как append-only лог: новые добавляются в конец, старые не удаляются сразу (retention по времени или размеру). Каждое сообщение имеет **offset** - монотонно возрастающий номер позиции в логе.

**Retention vs deletion:** Kafka не удаляет сообщения при прочтении (в отличие от RabbitMQ). Сообщения удаляются по retention policy (время или размер). Это позволяет нескольким независимым консьюмерам читать один topic, а также перечитывать исторические данные - критично для отладки и реплея событий.

Что происходит с сообщением в Kafka после того, как консьюмер его прочитал?

Partitions: горизонтальное масштабирование

Один лог - один поток записи. При 1 миллионе сообщений в секунду один брокер не справится. **Partition** - это решение: topic делится на N независимых логов, каждый на своём брокере. Продюсер пишет в разные partitions параллельно, консьюмеры читают параллельно.

Распределение по partitions определяется **ключом сообщения**: `partition = hash(key) % num_partitions`. Это гарантирует, что все сообщения с одним ключом (например, одним user_id) попадают в одну partition - а значит сохраняют порядок. Сообщения без ключа распределяются по round-robin.

**Replication и leader election:** каждая partition имеет одного leader-брокера и N-1 follower-ов. Все чтения и записи идут через leader. Если leader падает, Kafka автоматически выбирает нового из follower-ов (ISR - In-Sync Replicas). При replication_factor=3 кластер выдерживает отказ 2 из 3 брокеров без потери данных.

Kafka гарантирует порядок сообщений:

Consumer Groups: параллельное потребление

Topic с 12 partitions содержит события для аналитики: один сервис должен строить real-time дашборд, другой - записывать в Data Lake, третий - отправлять уведомления. Каждый из них должен получить все события. **Consumer Group** - механизм, позволяющий нескольким сервисам читать один topic независимо, и масштабировать каждый сервис горизонтально.

Внутри одной Consumer Group каждая partition назначается ровно одному консьюмеру - это обеспечивает параллельную обработку без дублирования. Разные Consumer Groups читают один и тот же topic независимо и получают все события.

**Rebalance:** когда консьюмер в группе падает или добавляется новый, Kafka проводит rebalance - перераспределение partitions между живыми консьюмерами. Во время rebalance обработка останавливается (stop-the-world). Kafka 2.4+ поддерживает Incremental Cooperative Rebalancing - только затронутые partitions перераспределяются, остальные продолжают работу.

Topic имеет 6 partitions. Consumer Group содержит 8 консьюмеров. Сколько консьюмеров будут активно читать данные?

Exactly-Once Semantics

Платёжный сервис обрабатывает транзакцию через Kafka. Сообщение получено, база данных обновлена, но при коммите offset-а Kafka временно недоступен. Консьюмер перезапустится и обработает сообщение снова - и транзакция задвоится. Это проблема **доставки сообщений**: at-most-once (потери), at-least-once (дубликаты), exactly-once (идеально, но сложно).

**Kafka Streams** обеспечивает exactly-once семантику автоматически при `processing.guarantee=exactly_once_v2`. Под капотом: транзакционный продюсер + атомарный commit offset + запись результата в выходной topic. Без Kafka Streams exactly-once требует ручного управления транзакциями или идемпотентной бизнес-логики (проверка дедупликации по event_id в БД).

Достаточно включить enable_idempotence=True на продюсере для полного exactly-once

Идемпотентный продюсер устраняет дубликаты только на стороне записи в Kafka (producer retry). Для полного exactly-once нужна транзакция, объединяющая чтение, обработку и запись - иначе дубликаты возникнут на стороне консьюмера.

Идемпотентность продюсера и exactly-once доставка - разные уровни гарантий. Продюсер может надёжно записать сообщение ровно раз, но консьюмер всё равно обработает его дважды при перезапуске без транзакционного commit-а offset-а.

At-least-once семантика означает, что:

Kafka: архитектура и паттерны

  • Topic - append-only лог событий; сообщения не удаляются при чтении, хранятся по retention policy
  • Partition = единица масштабирования и параллелизма; порядок гарантирован только внутри partition
  • Consumer Group: N консьюмеров делят partitions между собой; разные группы читают topic независимо
  • At-least-once (commit после обработки) - стандарт; exactly-once требует транзакций или идемпотентной логики
  • Идемпотентный продюсер устраняет дубли записи; полный exactly-once нужно обеспечивать транзакцией

Связанные темы

Kafka - центр real-time инфраструктуры Big Data:

  • Stream Processing: Flink и Spark Streaming — Kafka как источник данных для потоковой обработки
  • Data Lake архитектура — Kafka -> Delta Lake/Iceberg: стандартный паттерн загрузки событий
  • Delta Lake, Iceberg, Hudi — Kafka Sink Connector для потоковой записи в table formats

Вопросы для размышления

  • При каком условии увеличение числа partitions не ускорит обработку - и как это диагностировать?
  • Почему exactly-once в Kafka не защищает от дублей, если консьюмер пишет результат во внешнюю БД без дополнительных гарантий?
  • Как правильно выбрать ключ партиционирования: по user_id или по event_type - и от чего это зависит?

Связанные уроки

  • ds-01-intro
Kafka: архитектура и паттерны

0

1

Войти