В прошлом уроке мы разобрались со Spark — distributed engine для обработки. Но для streaming-сценариев одного Spark недостаточно: нужна система доставки сообщений между сервисами и обработчиками. Самая популярная такая система в мире DE — Apache Kafka.
Kafka родилась в LinkedIn в 2010 году как решение проблемы передачи логов между сервисами. К 2014 году стала open-source в Apache, и сегодня её используют практически все крупные компании, работающие с реальным временем: Netflix, Spotify, Uber, Airbnb, Tesla, банки, маркетплейсы.
Этот урок даёт концептуальный обзор. Мы НЕ погружаемся в репликацию, internals брокера, KRaft, Schema Registry, Kafka Connect, security, tuning. Для глубины есть отдельный kafka-course на платформе.
Что такое Kafka на одной странице
Kafka — это распределённый append-only лог сообщений. Сообщения упорядочены, разбиты на партиции, реплицированы для надёжности. Producers пишут сообщения, consumers читают.
Это не очередь (как RabbitMQ или AWS SQS), это лог. Разница важна:
- Очередь — сообщение прочитал -> оно удалено.
- Лог — сообщения хранятся независимо от чтений, разные consumers могут читать одни и те же сообщения, можно перечитать с начала.
Это свойство (replayability) делает Kafka backbone-ом streaming-архитектур.
Producers пишут в topic, broker сохраняет в партициях, consumers читают независимо, каждый со своим offset.
Один поток событий -> несколько независимых обработчиков. Каждый идёт своим темпом, ничего не блокирует.
Topics
Topic — это логический канал сообщений. Например:
orders— события создания/обновления заказов.user-clicks— клики пользователей на сайте.payments— события платежей.metrics.cpu— телеметрия CPU с серверов.
Сообщения публикуются в topic. Producers пишут в topic, consumers читают из topic. Один Kafka-кластер обычно держит сотни или тысячи topics для разных типов событий разных команд.
Partitions
Внутри topic есть partitions — упорядоченные последовательности сообщений. Каждый topic разбит на несколько партиций (например, 12 или 50 или 100), и партиции распределены по брокерам.
topic: orders
├── partition 0 -> broker 1 (leader), broker 2 (replica), broker 3 (replica)
├── partition 1 -> broker 2 (leader), broker 3 (replica), broker 1 (replica)
├── partition 2 -> broker 3 (leader), broker 1 (replica), broker 2 (replica)
└── ...
Внутри партиции сообщения строго упорядочены. Между партициями порядок не гарантируется.
Когда producer пишет сообщение, он выбирает партицию двумя способами:
- По ключу —
partition = hash(key) mod partition_count. Все сообщения с одинаковым ключом попадают в одну партицию, и потому идут в строгом порядке. Это используется для бизнес-сценариев типа «все события одного user_id обрабатываются в порядке». - Round-robin — без ключа партиции выбираются равномерно. Это даёт максимальный параллелизм, но теряет порядок.
Партиционирование — это и масштабирование, и порядок одновременно. Больше партиций = больше параллелизма consumers. Партиция по ключу = строгий порядок для всех сообщений этого ключа. Эти две цели иногда конфликтуют — приходится их балансировать.
Offsets
Каждое сообщение внутри партиции имеет offset — последовательный номер от 0. Это позиция сообщения в логе.
partition 0:
offset 0: {user: A, event: click}
offset 1: {user: B, event: purchase}
offset 2: {user: A, event: click}
offset 3: {user: C, event: view}
...
Consumer хранит, до какого offset он дочитал в каждой партиции. Это и есть позиция чтения. Если consumer упал и перезапустился — он продолжает с последнего сохранённого offset, ничего не пропустив. Если нужно перечитать историю — можно сбросить offset на 0 и пройти лог заново.
Offset управляется одним из двух способов:
- Commit’ы в Kafka. Consumer периодически коммитит offset в специальный internal topic
__consumer_offsets. Kafka хранит, на каком offset стоит consumer group. - External tracking. Consumer сохраняет offset во внешнем хранилище — например, в DWH или в файл. Это даёт более гибкий контроль (например, atomic commit вместе с записью в DWH).
Producers
Producer — это код, который пишет сообщения в Kafka. Пример на Python:
from confluent_kafka import Producer
producer = Producer({'bootstrap.servers': 'kafka-broker:9092'})
producer.produce(
topic='orders',
key=str(user_id), # ключ для партиционирования
value=json.dumps({ # тело сообщения
'order_id': 12345,
'user_id': user_id,
'amount': 99.50,
'created_at': '2026-05-17T14:23:00Z'
})
)
producer.flush()
Producer обычно живёт внутри другого сервиса (например, сервиса заказов или mobile app backend). Он работает асинхронно: сообщение публикуется в фоновый буфер, отправляется в Kafka, на ack обновляется метрика. Если broker недоступен, producer ретраит.
Consumers и consumer groups
Consumer — это код, который читает сообщения. Пример на Python:
from confluent_kafka import Consumer
consumer = Consumer({
'bootstrap.servers': 'kafka-broker:9092',
'group.id': 'dwh-loader', # consumer group
'auto.offset.reset': 'earliest' # с какого offset начинать, если ничего не закоммичено
})
consumer.subscribe(['orders'])
while True:
msg = consumer.poll(timeout=1.0)
if msg is None:
continue
if msg.error():
print(f"Error: {msg.error()}")
continue
process_order(json.loads(msg.value()))
consumer.commit(asynchronous=False)
Самое важное здесь — consumer group. Это группа consumer-процессов, которые разделяют работу по одному topic.
Внутри одной consumer group партиции делятся между процессами. Между разными группами — независимое чтение.
Ключевые свойства consumer groups:
- Внутри группы партиции делятся между consumer-ами. Если в группе N consumers и в topic M партиций — каждому достанется M/N партиций.
- Между группами чтение независимо. Каждая группа имеет свой offset, читает один и тот же поток событий своим темпом.
- Авто-rebalance: если один consumer упал, оставшиеся в группе подхватывают его партиции.
Если consumers больше, чем партиций — лишние простаивают. Поэтому количество партиций определяет максимальный параллелизм потребления.
Replication
Kafka реплицирует партиции для надёжности. Каждая партиция имеет leader (брокер, через которого идёт read/write) и followers (реплики). Если leader падает, один из followers становится новым leader-ом.
Параметр replication.factor (обычно 3) означает, что каждое сообщение хранится на 3 брокерах. Если 2 упадут одновременно, данные не потеряются. В облачных managed-сервисах (Confluent Cloud, AWS MSK) replication настроен по умолчанию.
Простой пример: producer пишет, consumer читает
Соберём всё в один сценарий. Есть topic user-clicks, сервис сайта пишет события клика, downstream consumer считает количество кликов по странам.
Producer (на сайте, JS-backend):
producer.produce(
topic='user-clicks',
key=str(user_id),
value=json.dumps({
'user_id': user_id,
'country': 'RU',
'page': '/products/42',
'timestamp': '2026-05-17T14:23:00Z'
})
)
Consumer (отдельный сервис аналитики):
from collections import defaultdict
counts = defaultdict(int)
while True:
msg = consumer.poll(1.0)
if msg is None: continue
event = json.loads(msg.value())
counts[event['country']] += 1
if total_processed % 1000 == 0:
print(counts)
consumer.commit()
Этот consumer вечно крутится и подсчитывает события. При перезапуске продолжает с последнего commit. Не теряет события (at-least-once), но может обработать одно событие дважды при сбое (нужен idempotent processing для exactly-once).
Где Kafka в DE-стеке
Kafka типично занимает позицию backbone для streaming:
- Source ingestion. События приложений, IoT-датчики, CDC из OLTP-баз (через Debezium) пишутся в Kafka.
- Stream processing. Flink, Spark Structured Streaming, Kafka Streams читают Kafka, обрабатывают, пишут в другие topics или sinks.
- DWH ingestion. Kafka Connect, Snowflake Kafka Connector, BigQuery Subscriber льют события в DWH.
- Operational decoupling. Сервисы общаются через Kafka, не зная друг о друге напрямую — event-driven architecture.
Что осталось за кадром
Глубокая Kafka — это:
- KRaft и ZooKeeper-less архитектура. Современная Kafka не требует ZooKeeper для координации.
- Schema Registry и Avro. Управление схемами сообщений и обратной совместимостью.
- Exactly-once семантика. Транзакции Kafka, idempotent producers.
- Kafka Connect. Готовые connectors к десяткам источников и sinks.
- Tiered storage. Холодные данные на S3, горячие на брокерах.
- Security. SASL, mTLS, ACL.
- Tuning. Размер сообщений, batch.size, compression, num.partitions, replication.factor.
В нашем kafka-course на платформе мы разбираем это с диаграммами и hands-on lab-ом. Если планируешь работать со streaming серьёзно — обязательно туда.
Kafka internals: distributed commit log, партиции и репликация Debezium: CDC из OLTP в Kafka без кодаБазовое понимание Kafka — обязательный навык для middle/senior DE. На собеседованиях часто спрашивают partitions, consumer groups, offset management и схему replication. Это не глубокие internals, но обязательный концептуальный минимум.
Попробуй сам
Подумай о приложении, которое ты знаешь — мессенджер, банк, маркетплейс. Какие топики в нём могли бы быть? messages, user-actions, payments, notifications? Какие consumers читают каждый топик? notification-service, dwh-loader, fraud-detector, analytics? Это упражнение даёт почувствовать роль Kafka — она склеивает десятки независимых сервисов через единый поток событий.