Real-Time Backend

Kafka для real-time

Как LinkedIn прокачивает триллион сообщений в день, не теряя ни одного - и при чём здесь обычный текстовый файл на диске?

  • LinkedIn обрабатывает 1 триллион+ сообщений в день через Kafka - активность пользователей, клики, обновления ленты - всё идёт через единый лог событий
  • Uber использует Kafka Streams для расчёта surge pricing в реальном времени: события поездок → агрегация спроса/предложения по районам → динамический коэффициент тарифа за секунды
  • Cloudflare логирует 10 миллионов запросов в секунду через Kafka: edge-узлы пишут события, WAF и аналитика читают независимо без блокировок друг друга
  • Netflix использует Kafka для трекинга прогресса просмотра: даже при падении consumer группы данные не теряются - сервис догоняет пропущенные события по retention

Kafka Realtime

Apache Kafka - это распределённый лог событий, который хранит сообщения на диске и отдаёт их потребителям в порядке записи. В отличие от RabbitMQ или Google Pub/Sub, сообщения после чтения не удаляются: они лежат столько, сколько задаёт `retention.ms`. Это позволяет нескольким независимым сервисам читать один и тот же поток данных в разное время.

LinkedIn, где Kafka и родился, прокачивает через него свыше 1 триллиона сообщений в день - активность пользователей, клики, обновления фида. Cloudflare использует Kafka для логирования 10 миллионов запросов в секунду: каждый edge-узел пишет события в топик, аналитика и WAF-движок читают независимо, не блокируя друг друга.

Ключевое отличие от Pub/Sub: **retention**. Kafka хранит историю - можно перемотать на любой offset и воспроизвести события заново. Это критично для аудита, replay-тестирования новых сервисов и recovery после инцидента.

Cloudflare прокатила через Kafka WAF-движок и новый ML-аномалию-детектор. Оба читают топик `requests`. Если ML-детектор упал на 2 часа и потом поднялся - что произойдёт?

Consumer Lag

Consumer lag - разница между последним записанным offset (log end offset) и offset, который consumer group уже подтвердил. Если producer пишет 50 000 событий/сек, а consumer обрабатывает 40 000 - лаг растёт на 10 000 событий каждую секунду. Когда это происходит на Uber в момент surge pricing, задержка между поездкой и пересчётом тарифа начинает ощущаться пользователями.

Партиция 1 отстаёт на 35 000 событий - скорее всего, consumer, привязанный к ней, перегружен или завис. Мониторинг лага через Prometheus + `kafka_consumer_group_lag` метрику позволяет поставить алерт: если лаг превышает N и не снижается K секунд - страница on-call инженера.

  • **Slow processing** - consumer тратит на одно сообщение больше времени, чем producer генерирует следующее
  • **Consumer crash** - одна инстанция упала, rebalance занял несколько секунд, за это время лаг вырос
  • **Traffic spike** - burst входящих событий превысил пропускную способность consumer группы
  • **GC pause** - длинная Stop-the-World пауза у JVM-consumer заморозила обработку на сотни миллисекунд

Uber мониторит лаг consumer group `surge-pricing`. В 18:00 лаг вырос с 500 до 80 000 за 5 минут и продолжает расти. Что это означает?

Partitioning

Партиция - единица параллелизма в Kafka. Одну партицию читает ровно один consumer из группы одновременно. Если топик `ride-events` имеет 12 партиций, consumer group из 12 инстанций читает параллельно - каждая берёт по одной партиции. 13-я инстанция будет стоять без работы: партиций не хватит.

Ключ партиционирования определяет, в какую партицию попадает сообщение. Uber шардирует по `driver_id`: все события одного водителя гарантированно попадают в одну партицию, а значит обрабатываются в порядке прихода одним consumer-ом. Это критично для корректного расчёта маршрута и стоимости поездки.

**Количество партиций нельзя уменьшить** - только увеличить. При увеличении партиций тот же partition key может попасть в другую партицию (hash % new_count изменился), что нарушает ordering-гарантии. Планировать партиционирование нужно на этапе создания топика с запасом.

Uber использует `driver_id` как partition key. Почему именно такой выбор критичен для surge pricing?

Kafka Tuning

Kafka предоставляет три конфигурации надёжности producer-а через параметр `acks`. При `acks=0` producer не ждёт подтверждения - максимальный throughput, нулевые гарантии. При `acks=1` лидер-брокер подтверждает запись, но если он упадёт до репликации - сообщение потеряно. При `acks=all` все реплики ISR (in-sync replicas) подтверждают запись - полная durability, latency выше.

  1. **`linger.ms`** - время ожидания перед отправкой батча. Увеличение с 0 до 5 мс повышает throughput в 5-10 раз при минимальном росте latency
  2. **`batch.size`** - максимальный размер батча в байтах. По умолчанию 16KB; для высоконагруженных топиков - 64-256KB
  3. **`compression.type`** - snappy снижает объём на 50-70% при минимальной CPU нагрузке; lz4 быстрее; gzip компактнее но медленнее
  4. **`min.insync.replicas`** - минимум реплик для подтверждения при acks=all. Значение 2 из 3 реплик - стандарт для production

Больше партиций всегда означает лучшую производительность

Каждая партиция - это файл на диске и TCP-соединение; слишком много партиций увеличивает latency failover и нагрузку на ZooKeeper/KRaft

При падении брокера Kafka переназначает лидеров для всех его партиций. 10 000 партиций на брокер означают 10 000 операций переназначения - failover может занять десятки секунд. LinkedIn рекомендует не более 4000 партиций на брокер и 200 000 на кластер.

Stripe обрабатывает платёжные события через Kafka. Какую комбинацию `acks` и `idempotent` следует использовать для топика `payment-transactions`?

Итоги

  • Kafka хранит сообщения как append-only лог с retention-периодом - несколько consumer groups читают один топик независимо, каждая со своим offset
  • Consumer lag - разница между тем, что записано, и тем, что прочитано группой; растущий лаг означает, что consumer не успевает за producer-ом
  • Количество партиций определяет максимальный параллелизм: один consumer из группы - одна партиция; partition key гарантирует ordering событий одной сущности
  • Для надёжности финансовых данных: acks=all + idempotent=true; для аналитических логов: acks=1 + крупные батчи + сжатие

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

Kafka встраивается в более широкие паттерны real-time архитектуры

  • Горизонтальное масштабирование — Партиционирование Kafka - конкретная реализация sharding для потоков событий
  • Event-driven архитектура — Kafka - основной transport layer для event sourcing и CQRS паттернов
  • Мониторинг и observability — Consumer lag - ключевая метрика health check real-time пайплайна

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

  • Если бы нужно было выбрать между Kafka и Redis Pub/Sub для системы аналитики с 10+ независимыми потребителями данных - какой фактор стал бы решающим?
  • Uber шардирует события по driver_id. Что произойдёт с ordering-гарантиями, если завтра добавить партиций в топик без миграции данных?
  • В каком сценарии высокий consumer lag - нормальная ситуация, а не повод для алерта?

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

  • net-55-message-queues
Kafka для real-time

0

1

Войти