Kafka: архитектура и базовые принципы — Часть 1

Содержание
Коротко: Kafka лучше понимать не как обычную очередь, а как распределённый журнал событий. В первой части разбираем, зачем она нужна, когда оправдана и из каких базовых сущностей состоит.

Если посмотреть на архитектуру крупной IT-компании — Aviasales, Uber, Avito, Яндекс или Ozon — с большой вероятностью где-нибудь между сервисами окажется Kafka. Иногда аккуратно встроенная в платформу данных, иногда как критичный компонент, который все боятся трогать, потому что «оно же работает».

Kafka давно перестала быть просто брокером сообщений и стала фундаментом современных распределённых систем.

Эта серия статей — подробное руководство по Kafka. Мы не только разберём основные компоненты, но и поймём, почему Kafka настолько быстрая, как она обеспечивает отказоустойчивость и что нужно учитывать при проектировании production-потоков.

Серия ориентирована на современные версии Apache Kafka 3.x и 4.x. Базовые принципы — Topic, Partition, Producer, Consumer, Offset, Consumer Group, репликация и retention — актуальны для обеих веток. Там, где речь идёт об эксплуатации кластера, я ориентируюсь на Kafka 4.x и KRaft, а не на устаревший ZooKeeper-режим.

Что такое Kafka

Apache Kafka — это распределённая платформа потоковой передачи событий (Distributed Event Streaming Platform).

Если говорить простыми словами, Kafka — это распределённый журнал событий: одни приложения записывают в него сообщения, а другие читают их в своём темпе. Большинство новичков представляет Kafka как обычную очередь, но это только часть картины. Правильнее думать о Kafka как о логе, куда новые записи постоянно добавляются в конец. Например:

----------------------------------
User Registered
Order Created
Payment Success
Email Sent
Product Viewed
Review Added
----------------------------------

Но этим её возможности не ограничиваются.

Kafka умеет:

  • передавать данные между микросервисами;
  • сохранять журнал событий для повторного чтения и восстановления;
  • масштабироваться горизонтально под высокую нагрузку;
  • выдерживать очень высокий поток сообщений;
  • восстанавливаться после отказа серверов (в следующих частях мы размеберем детально)

Именно поэтому Kafka используют не только как очередь сообщений, но и как основу для событийных потоков между сервисами, аналитикой и хранилищами данных.

Проблема прямых вызовов между сервисами

Представим интернет-магазин.

Когда пользователь оформляет заказ, необходимо:

  • списать деньги;
  • уменьшить остаток товара;
  • отправить письмо;
  • начислить бонусы;
  • обновить аналитику;
  • отправить уведомление в мобильное приложение.

Самое очевидное решение выглядит так: сервис заказов вызывает каждый зависимый сервис напрямую.

alt text

Поначалу всё выглядит нормально. Демка работает, бизнес доволен, никто ещё не произнёс слово «интеграция» с болью в голосе. Но проходит полгода, и появляются новые сервисы:

alt text

Получается классическая проблема сильной связанности (tight coupling): один сервис знает слишком много о внутренних процессах других сервисов.

Любое изменение затрагивает множество команд и контрактов. Временная недоступность одного сервиса начинает ломать весь пользовательский сценарий, хотя сам заказ уже мог быть успешно создан.

С Kafka сервис заказов может не вызывать всех напрямую. Он публикует событие, а остальные сервисы самостоятельно подписываются на него и обрабатывают в своём темпе. Пусть вас не пугает эта схема — в следующих частях мы подробно разберём, как она работает внутри.

alt text

Такой подход даёт несколько преимуществ.

  1. Сервис заказов не знает, кто будет читать событие.
  2. Можно добавить новый сервис без изменения существующего кода.
  3. Если один из сервисов временно недоступен, остальные продолжают работать.

Именно эта слабая связанность сделала Kafka настолько популярной.

Когда Kafka действительно нужна

Важно понимать: любое усложнение архитектуры должно быть оправданным. Kafka часто пытаются использовать там, где она не нужна, и это быстро превращается в лишнюю операционную сложность.

Здесь хорошо работает принцип KISS: если задачу можно решить проще, лучше начать с более простой архитектуры. Если же у вас небольшой интернет-магазин, CRM на 20 пользователей, простой REST API, монолит без высокой нагрузки – то с вероятностью 100% внедрение Kafka будет просто выстрелом себе в ногу. Во многих случаях достаточно RabbitMQ, Redis Streams, фоновых задач или обычной PostgreSQL. Использование Kafka оправдано тогда, когда уже есть реальные требования к потоку событий, независимым потребителям, replay или масштабированию, а не просто желание взять технологию «на будущее».

Kafka отлично вам подойдет, если:

  • данные должны читать множество сервисов;
  • необходима асинхронная обработка;
  • требуется переживать пики нагрузки;
  • важен журнал всех событий;
  • нужно иметь возможность воспроизвести события с какого-то момента;
  • нагрузка достигает “миллионов сообщений”.

Фундаментальное отличие от очередей

В классической очереди сообщение исчезает после обработки.

alt text

В Kafka всё иначе. Consumer Group фиксирует позицию чтения: «мы прочитали до сообщения №15342».

🔎 Tip
Kafka хранит committed offset не в памяти consumer-а, а во внутреннем топике __consumer_offsets. Важно понимать, что позиция чтения относится к конкретной связке Consumer Group, Topic и Partition, а не ко всей группе целиком.

Сами записи остаются в логе до истечения retention, поэтому другая группа потребителей может прочитать их позже.

alt text

Тот же Consumer при необходимости может сбросить offset и прочитать историю заново. Именно это делает возможными:

  • Event Sourcing — подход, при котором Kafka хранит не только текущее состояние объекта, а последовательность событий, которые к нему привели. Например: OrderCreated → PaymentReceived → OrderShipped. Текущее состояние можно восстановить, проиграв события по порядку.
  • Replay — возможность повторно прочитать старые сообщения из Kafka. Consumer может изменить offset и заново обработать события. Полезно после исправления бага, изменения бизнес-логики или для построения нового представления данных из истории.
  • Stream Processing — обработка событий в реальном времени по мере их поступления. Например: Kafka → Flink/Kafka Streams → агрегация платежей за последние 5 минут → новый Kafka topic. Используется для фильтрации, агрегации, enrichment, join’ов и вычислений над потоками.
  • CDC (Change Data Capture) — перенос изменений из базы данных в Kafka. Например, Debezium читает PostgreSQL WAL/MySQL binlog и публикует INSERT/UPDATE/DELETE в Kafka. Другие системы могут реагировать на изменения БД без постоянного polling базы.
  • Аналитические системы — Kafka часто выступает транспортным слоем между источниками данных и аналитическим хранилищем. Например: микросервисы → Kafka → ClickHouse. Это позволяет собирать большие потоки событий, независимо масштабировать producers/consumers и строить near-real-time аналитику.

Первые термины Kafka

Перед тем как разбираться с внутренним устройством, давайте познакомимся с основными сущностями, чтобы нам говорить на одном языке.

Broker — сервер Kafka. На нём хранятся данные, журналы сообщений и метаданные партиций. Обычно брокеров несколько: так кластер получает больше пропускной способности и лучше переживает отказы.

Producer — приложение, которое отправляет сообщения. Это может быть:

  • backend;
  • мобильное приложение;
  • касса магазина;
  • банкомат;
  • IoT-датчик.

Topic — логическая категория сообщений. Producer пишет сообщение не на конкретный сервер, а в Topic. Дальше Kafka размещает запись в одной из партиций этого топика.

Например:

  • orders
  • payments
  • notifications
  • users

Consumer — приложение, которое читает сообщения. Самый простой пример — сервис рассылки уведомлений по созданному заказу.

Последнее, что нужно знать на данный момент: Producer и Consumer не обязаны знать друг о друге напрямую. Их связывает Topic.


На этом месте остановимся. Базовые сущности уже на столе, теперь можно перестать смотреть на Kafka как на «ещё одну очередь» и перейти к механике.

В следующей части разберём Partition — механизм, который позволяет Kafka масштабироваться, не превращая порядок сообщений в лотерею. Затем рассмотрим Consumer Groups и соберём первую полноценную архитектуру Kafka.