Архитектура и управление топиками в Apache Kafka

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

1. Основы топиков: партиции, сегменты и структура логов

Основы топиков: партиции, сегменты и структура логов

Добро пожаловать в курс «Архитектура и управление топиками в Apache Kafka». Это первая статья, и мы начнем с фундамента, на котором держится вся экосистема Kafka. Многие воспринимают Kafka просто как «очередь сообщений», но технически это распределенный журнал фиксации изменений (distributed commit log). Чтобы понять, как управлять данными, масштабировать систему и обеспечивать надежность, необходимо разобраться в анатомии топика.

В этой статье мы спустимся с логического уровня (топик) на физический уровень (сегменты на диске) и разберем, как именно Kafka хранит ваши данные.

Что такое Топик?

На самом высоком уровне абстракции находится Топик (Topic). Топик — это именованный поток записей (сообщений). Если проводить аналогию с базами данных, топик похож на таблицу в реляционной базе данных или на папку в файловой системе.

Топики в Kafka являются логическими сущностями. Вы отправляете сообщения в топик «orders» (заказы) или «logs» (логи), и потребители (consumers) подписываются на эти топики, чтобы читать данные. Однако сам по себе топик не является единым монолитным файлом.

Ключевые характеристики топика:

  • Многопользовательский доступ: У топика может быть ноль, один или много производителей (producers) и потребителей.
  • Долговечность: Данные в топике хранятся определенное время (retention period), даже если они уже были прочитаны.
  • Партиции: Единица масштабирования

    Если бы топик хранился на одном сервере в виде одного файла, мы бы быстро столкнулись с ограничениями по скорости записи и объему диска. Чтобы решить эту проблему, Kafka разбивает топик на Партиции (Partitions).

    Партиция — это упорядоченная, неизменяемая последовательность записей, к которой постоянно добавляются новые данные. Это и есть тот самый «журнал» (log).

    !Топик, разделенный на три партиции, обеспечивающий параллельную обработку данных.

    Зачем нужны партиции?

  • Масштабируемость (Scalability): Разные партиции одного топика могут находиться на разных серверах (брокерах) кластера. Это позволяет топику хранить больше данных, чем помещается на один сервер.
  • Параллелизм (Parallelism): Партиция — это единица параллелизма. Если у топика 4 партиции, вы можете запустить 4 потребителя одновременно, каждый из которых будет читать свою партицию. Вы не можете иметь больше активных потребителей в одной группе, чем количество партиций.
  • Гарантия порядка (Ordering Guarantee)

    Это один из самых важных концептов в Kafka, который часто вызывает недопонимание.

    > Kafka гарантирует порядок сообщений ТОЛЬКО в пределах одной партиции.

    Если вы отправили сообщение А, а затем сообщение Б в одну и ту же партицию, то потребитель гарантированно прочитает сначала А, потом Б. Однако, если сообщения попали в разные партиции, глобальный порядок между ними не гарантируется.

    Структура лога и Оффсеты

    Каждая запись в партиции получает последовательный порядковый номер, который называется смещением или оффсетом (offset).

    Оффсет (Offset)

    Оффсет — это просто целое число (64-битный integer), которое монотонно возрастает. Первое сообщение в партиции имеет оффсет 0, следующее — 1, и так далее.

    Важные свойства оффсета: * Уникальность: Оффсет уникален только в рамках своей партиции. Сообщение с оффсетом 5 в Партиции 0 и сообщение с оффсетом 5 в Партиции 1 — это совершенно разные данные. * Идентификация: Оффсет служит единственным идентификатором сообщения внутри партиции. Потребители используют оффсеты, чтобы «запомнить», где они остановились при чтении.

    Неизменяемость (Immutability)

    Лог в Kafka является append-only (только для добавления). Как только запись записана в партицию и получила оффсет, она становится неизменяемой. Вы не можете отредактировать сообщение или удалить его из середины очереди, как это можно сделать в базе данных SQL.

    Это ограничение дает колоссальное преимущество в производительности. Запись в конец файла — это последовательная операция ввода-вывода (Sequential I/O), которая на магнитных жестких дисках (HDD) работает в разы быстрее, чем случайный доступ (Random Access).

    Сегменты: Физическое хранение

    Мы выяснили, что топик делится на партиции. Но как партиция выглядит на жестком диске сервера? Если партиция будет жить вечно, она превратится в один гигантский файл, который неудобно хранить и очищать.

    Поэтому каждая партиция физически разбивается на Сегменты (Segments).

    Файловая структура

    На диске брокера каждая партиция представлена директорией. Имя директории обычно имеет формат <topic_name>-<partition_id>. Например, orders-0.

    Внутри этой директории находятся файлы сегментов. Сегмент состоит из двух основных файлов:

  • .log файл: Содержит сами данные (сообщения).
  • .index файл: Содержит индекс, который сопоставляет логические оффсеты с физической позицией байтов в файле .log.
  • !Физическая структура сегментов партиции на диске.

    Активный сегмент (Active Segment)

    В любой момент времени для записи доступен только один сегмент — активный сегмент. Все новые сообщения пишутся в него. Остальные (старые) сегменты закрыты для записи и используются только для чтения.

    Когда активный сегмент достигает определенного предела (по размеру или по времени), он «закрывается» (rolled), и создается новый активный сегмент.

    Имя файла сегмента — это оффсет первого сообщения в этом сегменте. Например: * 00000000000000000000.log (содержит сообщения с оффсетами 0...1023) * 00000000000000001024.log (содержит сообщения с оффсетами 1024...)

    Индексы

    Зачем нужен файл .index? Представьте, что потребитель хочет прочитать сообщение с оффсетом 5000. Без индекса Kafka пришлось бы сканировать .log файл с самого начала, чтобы найти нужное сообщение. Это медленно.

    Индекс использует разреженную индексацию (sparse indexing). Он хранит указатели не на каждое сообщение, а, например, на каждые 4 КБ данных. Kafka находит в индексе ближайшую позицию, а затем сканирует небольшой участок лога до нужного сообщения.

    Жизненный цикл данных

    Понимание сегментов важно для понимания того, как Kafka удаляет старые данные. Kafka не удаляет сообщения по одному. Удаление происходит целыми сегментами.

    Существует две основные стратегии очистки (Retention):

  • По времени (Time-based): Удалять сегменты, в которых последнее сообщение старше N дней (например, 7 дней).
  • По размеру (Size-based): Если размер партиции превышает N гигабайт, удалять самые старые сегменты.
  • Важно помнить: активный сегмент никогда не удаляется, даже если срок хранения данных истек.

    Резюме

    Подведем итоги иерархии хранения данных в Kafka:

  • Кластер Kafka: Состоит из брокеров.
  • Топик: Логическая категория сообщений.
  • Партиция: Физическая часть топика, обеспечивающая порядок и масштабируемость. Это append-only лог.
  • Сегмент: Файл на диске, часть партиции. Единица управления хранением и очисткой данных.
  • Запись (Record): Единица данных с уникальным оффсетом внутри партиции.
  • В следующей статье мы подробно разберем, как производители (Producers) выбирают, в какую именно партицию отправить сообщение, и как работает балансировка нагрузки.

    2. Механизмы репликации: лидеры, фолловеры и гарантии сохранности данных

    Механизмы репликации: лидеры, фолловеры и гарантии сохранности данных

    В предыдущей статье мы подробно разобрали анатомию топика: как он делится на партиции и сегменты, и как эти файлы хранятся на диске. Мы выяснили, что партиционирование обеспечивает масштабируемость. Но что произойдет, если сервер (брокер), на котором хранится партиция, выйдет из строя? Если бы данные хранились в единственном экземпляре, мы бы их потеряли.

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

    Что такое Фактор Репликации?

    На уровне конфигурации топика вы определяете параметр, называемый Фактором репликации (Replication Factor). Он указывает, сколько копий каждой партиции должно существовать в кластере.

    * Если фактор репликации равен 1, то при падении брокера данные становятся недоступны (или теряются, если диск поврежден). * Стандартным промышленным стандартом является фактор репликации 3. Это означает, что каждая партиция существует в трех экземплярах на трех разных брокерах.

    !Распределение реплик одной партиции по трем разным брокерам для обеспечения отказоустойчивости.

    Анатомия репликации: Лидер и Фолловеры

    В Kafka реплики не равноправны. В любой момент времени для каждой партиции одна реплика назначается Лидером (Leader), а остальные становятся Фолловерами (Followers).

    Роль Лидера (Leader)

    Лидер — это «главная» копия партиции.

    * Все операции записи от производителей (Producers) всегда идут только к лидеру. * Все операции чтения потребителями (Consumers) по умолчанию также идут к лидеру (начиная с новых версий Kafka, возможно чтение с фолловеров, но это специфичный сценарий).

    Лидер отвечает за упорядочивание сообщений и присвоение им оффсетов.

    Роль Фолловера (Follower)

    Фолловеры — это пассивные реплики. Они не обрабатывают запросы от клиентов напрямую. Их единственная задача — как можно быстрее копировать (fetch) данные у лидера, чтобы оставаться в актуальном состоянии.

    Фактически, фолловер действует как особый вид потребителя: он отправляет запросы на чтение лидеру, получает пакеты сообщений и записывает их в свой локальный лог.

    In-Sync Replicas (ISR)

    Не все фолловеры одинаково полезны. Представьте, что один из фолловеров отключился от сети на час. Формально он все еще является репликой, но его данные безнадежно устарели. Если лидер упадет, мы не можем сделать этого отстающего фолловера новым лидером, иначе потеряем час данных.

    Чтобы решить эту проблему, Kafka вводит понятие ISR (In-Sync Replicas) — список синхронизированных реплик.

    ISR — это подмножество всех реплик, которые:

  • Живы и подключены к контроллеру.
  • Успевают копировать данные у лидера, не отставая слишком сильно.
  • Лидер всегда входит в ISR. Фолловеры входят в ISR только если они «успевают» за лидером.

    Критерий синхронизации

    Как Kafka понимает, что фолловер отстал? За это отвечает параметр replica.lag.time.max.ms (по умолчанию 30 секунд).

    Если фолловер не запрашивал данные у лидера или не успел догнать лог лидера в течение этого времени, он исключается из списка ISR. Как только он догонит лидера, он снова включается в ISR.

    [VISUALIZATION: Диаграмма множеств. Большой круг

    3. Управление хранением: Retention Policy и Log Compaction

    Управление хранением: Retention Policy и Log Compaction

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

    В этой статье мы изучим механизмы очистки данных (Cleanup Policies). В Kafka существует два фундаментально разных подхода к управлению жизненным циклом сообщений:

  • Удаление (Delete): Традиционный подход, основанный на времени или размере лога.
  • Компактизация (Compact): Подход, основанный на ключах сообщений, позволяющий хранить последнее актуальное состояние.
  • Понимание этих механизмов критически важно для проектирования системы, так как неправильная настройка может привести либо к потере важных данных, либо к переполнению дисков и остановке брокеров.

    Стратегия удаления (Retention Policy: Delete)

    Это стандартное поведение Kafka по умолчанию. Когда вы создаете топик, параметр cleanup.policy обычно установлен в значение delete. Суть этой стратегии проста: Kafka хранит данные до тех пор, пока не будет достигнут определенный лимит, после чего старые данные безвозвратно удаляются.

    Существует два основных триггера для удаления данных:

    1. Ограничение по времени (Time-based Retention)

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

    Основные настройки: * log.retention.hours: Количество часов хранения (по умолчанию 168 часов, то есть 7 дней). * log.retention.ms: То же самое, но в миллисекундах (имеет приоритет, если задано).

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

    > Это означает, что если у вас установлен срок хранения 24 часа, но сегмент закрывается только раз в неделю, данные могут фактически храниться намного дольше 24 часов, пока сегмент не закроется и не истечет его время.

    2. Ограничение по размеру (Size-based Retention)

    Этот триггер защищает ваши диски от переполнения, если поток данных внезапно возрастет. Вы задаете максимальный размер данных для одной партиции.

    Настройка: log.retention.bytes (по умолчанию -1, то есть безлимитно).

    Если размер партиции превышает установленный порог, Kafka начинает удалять самые старые сегменты, пока размер не вернется в допустимые рамки. Это гарантирует предсказуемое использование дискового пространства, но не гарантирует время хранения данных (при высокой нагрузке данные могут удаляться через несколько минут).

    !Визуализация процесса удаления старых сегментов при истечении времени хранения.

    Стратегия компактизации (Log Compaction)

    Второй подход — Log Compaction — кардинально отличается от простого удаления. Он используется, когда нам важна не история событий, а текущее состояние объекта.

    Представьте, что вы храните в Kafka профили пользователей. Пользователь может менять свой адрес 10 раз. Вам не нужны 9 старых адресов, вам нужен только последний, актуальный адрес.

    При включении cleanup.policy=compact, Kafka гарантирует, что для каждого уникального ключа (Key) сообщения в логе останется как минимум одно последнее значение.

    Как это работает?

    Лог партиции логически делится на две части:

  • Хвост (Tail): Очищенная часть лога. Здесь для каждого ключа хранится только одно, последнее значение. Оффсеты здесь идут не по порядку (так как промежуточные сообщения были удалены).
  • Голова (Head): Активная часть лога (как в обычном топике). Сюда пишутся новые сообщения. Здесь могут быть дубликаты ключей.
  • Специальный фоновый процесс в брокере, называемый Log Cleaner, периодически сканирует лог. Он создает карту последних оффсетов для каждого ключа и затем копирует сегменты, отбрасывая старые версии ключей, оставляя только последние.

    !Процесс компактизации лога: сохранение только последнего значения для каждого ключа.

    Удаление данных в Compacted Topic (Tombstones)

    Если компактизация хранит данные вечно (последнее значение), то как удалить ключ совсем? Например, пользователь удалил свой аккаунт.

    Для этого используется механизм Tombstone (Могильная плита). Вы отправляете сообщение с нужным ключом и значением null (пустое тело сообщения).

    Когда Log Cleaner видит сообщение с null, он понимает, что этот ключ нужно удалить. Сначала он удаляет все предыдущие значения этого ключа. Само сообщение-tombstone хранится еще некоторое время (настраивается параметром log.cleaner.delete.retention.ms), чтобы убедиться, что все потребители успели прочитать событие удаления, после чего оно тоже исчезает.

    Сценарии использования Log Compaction

  • Восстановление состояния: Если приложение упало, при перезапуске оно читает топик с начала. Благодаря компактизации, ему не нужно перечитывать миллионы устаревших обновлений, оно сразу получает актуальный срез данных.
  • Таблицы в потоках (KTable): В библиотеке Kafka Streams компактизированные топики используются для представления таблиц данных (ключ-значение).
  • Конфигурации: Хранение настроек системы, где важна только последняя версия конфига.
  • Комбинированная политика

    Интересный факт: можно включить обе политики одновременно: cleanup.policy=delete,compact.

    В этом случае данные компактизируются (убираются дубликаты ключей), но при этом также работает удаление по времени или размеру. Это полезно, если вы хотите иметь «снэпшоты» данных, но не хотите хранить их вечно.

    Настройка и иерархия конфигураций

    Параметры хранения можно задавать на двух уровнях:

  • Уровень брокера (Broker Level): В файле server.properties. Это настройки по умолчанию для всех новых топиков.
  • * Пример: log.retention.hours=168
  • Уровень топика (Topic Level): Можно переопределить настройки для конкретного топика при его создании или изменении. Настройки топика всегда имеют приоритет над настройками брокера.
  • Пример команды для изменения retention существующего топика:

    Влияние на производительность

    * Удаление (Delete): Очень дешевая операция. Удаление целого файла сегмента происходит мгновенно и почти не нагружает систему. * Компактизация (Compact): Требует ресурсов CPU и Disk I/O. Процесс Log Cleaner читает сегменты, сортирует ключи и перезаписывает новые сегменты. В сильно нагруженных кластерах это может влиять на производительность записи и чтения, поэтому Log Cleaner имеет настройки троттлинга (ограничения скорости).

    Резюме

    Эффективное управление хранением — залог здоровья кластера Kafka.

    * Используйте Retention Policy (Delete) для потоков событий (логи, метрики, транзакции), где важна история за определенный период. * Используйте Log Compaction для объектов, имеющих состояние (профили, настройки, балансы), где важно только последнее значение ключа. * Помните, что удаление происходит посегментно, а не по одному сообщению.

    В следующей статье мы перейдем от хранения к передаче данных и разберем гарантии доставки сообщений: At-most-once, At-least-once и Exactly-once.

    4. Продюсеры и консюмеры: распределение данных по партициям и семантика доставки

    Продюсеры и консюмеры: распределение данных по партициям и семантика доставки

    Мы прошли долгий путь, изучая внутренности Kafka. Мы знаем, как топики нарезаются на партиции, как партиции хранятся в сегментах на диске и как репликация защищает нас от потери данных. Но пока наша система статична. Чтобы она ожила, нам нужны те, кто создает данные (Producers), и те, кто их обрабатывает (Consumers).

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

    Продюсер: Стратегии партиционирования

    Когда вы отправляете сообщение в Kafka, вы, как правило, указываете название топика, ключ (опционально) и значение. Но топик — это логическая абстракция. Физически сообщение должно попасть в конкретную партицию на конкретном брокере. Кто принимает это решение? Это делает Продюсер на стороне клиента, еще до отправки данных по сети.

    Существует три основных сценария распределения:

    1. Сообщения без ключа (Key = null)

    Если вы не указываете ключ сообщения, Kafka старается распределить нагрузку равномерно между всеми доступными партициями. Раньше для этого использовался простой алгоритм Round-Robin (по кругу: партиция 0, партиция 1, партиция 2...).

    Однако в современных версиях Kafka используется более эффективный подход — Sticky Partitioning («Липкое» партиционирование).

    Продюсер накапливает сообщения в пакеты (батчи). Вместо того чтобы отправлять каждое сообщение в новую партицию (что увеличивает накладные расходы и количество сетевых запросов), продюсер «прилипает» к одной партиции и наполняет батч для нее. Как только батч заполнен или вышло время ожидания, он отправляется, а продюсер переключается («прилипает») к следующей партиции.

    > Это значительно повышает пропускную способность и уменьшает задержки (latency) за счет отправки меньшего количества, но более крупных пакетов данных.

    2. Сообщения с ключом (Key != null)

    Если у сообщения есть ключ (например, user_id, order_id или sensor_id), логика меняется. Главная цель здесь — гарантия порядка.

    Все сообщения с одинаковым ключом обязаны попасть в одну и ту же партицию. Почему? Потому что Kafka гарантирует порядок чтения только в рамках партиции. Если события по одному пользователю размажутся по разным партициям, вы можете получить ситуацию, когда событие «Пользователь создан» придет позже события «Пользователь обновил профиль».

    Для выбора партиции используется формула хеширования:

    Где: * — номер целевой партиции. * — хеш-функция от ключа сообщения (обычно MurmurHash2). * — общее количество партиций в топике. * — операция взятия остатка от деления.

    Благодаря этой математической детерминированности, один и тот же ключ всегда дает один и тот же номер партиции (пока количество партиций не изменится).

    !Визуализация стратегий партиционирования: по ключу и sticky-partitioning.

    3. Custom Partitioner

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

    Надежность отправки: Параметр acks

    Продюсер отправил сообщение. Как он узнает, что Kafka его успешно сохранила? За это отвечает параметр конфигурации acks (acknowledgments — подтверждения).

  • acks=0 (Fire and Forget): Продюсер отправляет данные и не ждет ответа. Это обеспечивает максимальную скорость, но нулевую надежность. Если брокер упал в момент приема, данные потеряны, и вы об этом не узнаете.
  • acks=1 (Leader only): Продюсер ждет подтверждения только от Лидера партиции. Если Лидер записал сообщение, он отвечает «ОК». Это баланс между скоростью и надежностью. Но если Лидер упадет сразу после ответа, а фолловеры не успели скопировать данные — сообщение исчезнет.
  • acks=all (или -1): Самый надежный вариант. Лидер ждет, пока сообщение запишут все синхронизированные реплики (ISR), и только потом отвечает продюсеру. Это гарантирует, что данные не пропадут, пока жив хотя бы один брокер из списка ISR.
  • Консюмеры и Консюмер-группы

    Теперь перейдем к чтению. В Kafka чтение масштабируется иначе, чем в традиционных очередях сообщений (Message Queues). Главная концепция здесь — Consumer Group (Группа потребителей).

    Consumer Group — это объединение нескольких консюмеров, которые совместно читают один топик. Группа нужна для параллельной обработки данных.

    Правило масштабирования

    Kafka распределяет партиции топика между участниками группы. Работает жесткое правило:

    > Одну партицию в один момент времени может читать только один консюмер из одной группы.

    Это сделано для того, чтобы гарантировать порядок обработки. Если бы одну партицию читали два консюмера одновременно, они бы конкурировали за сообщения, и порядок был бы нарушен.

    Следствия этого правила: * Если консюмеров меньше, чем партиций: один консюмер может читать несколько партиций. * Если консюмеров равно количеству партиций: идеальный баланс, каждый читает свою партицию. * Если консюмеров больше, чем партиций: лишние консюмеры будут простаивать (idle). Они не получат ни одной партиции.

    !Иллюстрация ограничения масштабируемости консюмеров количеством партиций.

    Ребалансировка (Rebalancing)

    Что происходит, если один консюмер падает или вы добавляете нового? Запускается процесс ребалансировки. Группа останавливает чтение, и партиции перераспределяются между живыми участниками заново. Это критический момент, так как во время ребалансировки обработка данных временно останавливается (stop-the-world в старых версиях протокола, в новых — более оптимизированные алгоритмы).

    Семантика доставки (Delivery Semantics)

    В распределенных системах вопрос «доставлено ли сообщение?» не имеет простого ответа «да/нет». Существует три уровня гарантий, которые зависят от того, в какой момент консюмер фиксирует (коммитит) свой оффсет.

    Напомним: Оффсет — это метка, говорящая «я прочитал всё до этого места».

    1. At-most-once (Максимум один раз)

    Сообщения могут потеряться, но никогда не будут дублироваться.

    * Механизм: Консюмер читает пакет сообщений, сразу коммитит оффсет, и только потом начинает обрабатывать данные. * Риск: Если консюмер упадет во время обработки (после коммита), то при перезапуске он начнет читать со следующего оффсета. Те сообщения, которые он взял, но не успел обработать, будут потеряны навсегда.

    2. At-least-once (Минимум один раз)

    Сообщения никогда не теряются, но могут дублироваться. Это стандарт де-факто для большинства систем.

    * Механизм: Консюмер читает данные, обрабатывает их (сохраняет в БД, отправляет письма), и только после успешной обработки коммитит оффсет. Риск: Если консюмер упадет после обработки, но до* коммита, то при перезапуске он снова прочитает те же самые сообщения и обработает их повторно. Ваше приложение должно быть идемпотентным (уметь корректно обрабатывать дубли).

    3. Exactly-once (Ровно один раз)

    Святой Грааль распределенных систем. Каждое сообщение обрабатывается ровно один раз, без потерь и дублей.

    В Kafka это достигается комбинацией двух механизмов:

  • Idempotent Producer: Продюсер присваивает каждому сообщению уникальный ID последовательности. Если из-за сетевой ошибки продюсер отправит дубль, брокер его отбросит.
  • Transactions: Позволяет атомарно записать данные в несколько топиков (consume-process-produce паттерн). Либо всё записалось, либо ничего.
  • Для включения этого режима на продюсере достаточно выставить enable.idempotence=true (по умолчанию true в новых версиях).

    Резюме

    В этой статье мы соединили точки между созданием и потреблением данных:

    * Продюсеры используют ключи для гарантии порядка (хеширование) или Sticky Partitioning для эффективности. * Acks управляет балансом между надежностью и скоростью записи. * Консюмер-группы позволяют масштабировать чтение, но ограничены количеством партиций. Семантика доставки (At-least-once vs At-most-once) зависит от того, когда* вы коммитите оффсеты.

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

    5. Администрирование топиков: создание, изменение конфигурации и мониторинг метрик

    Администрирование топиков: создание, изменение конфигурации и мониторинг метрик

    Мы прошли большой путь, изучая внутреннее устройство Apache Kafka. Мы разобрали, как данные физически хранятся в сегментах, как репликация защищает от сбоев и как продюсеры выбирают партиции. Теперь настало время перейти от теории к практике эксплуатации.

    Как администратор или разработчик, вы будете регулярно сталкиваться с необходимостью создавать новые топики, менять их настройки «на лету» и следить за здоровьем кластера. В этой статье мы разберем основные инструменты управления (CLI tools) и ключевые метрики, которые отделяют стабильную систему от падающей.

    Инструментарий администратора

    Kafka поставляется с набором bash-скриптов, которые находятся в папке bin/ вашего дистрибутива. Это ваши главные инструменты. Мы сосредоточимся на двух самых важных:

  • kafka-topics.sh — для создания, удаления, просмотра и изменения структуры топиков.
  • kafka-configs.sh — для управления параметрами конфигурации (retention, размер сообщений и т.д.).
  • Создание топика: больше, чем просто имя

    Создание топика — это момент, когда вы закладываете фундамент его производительности и надежности. Команда выглядит просто, но параметры требуют осмысления.

    Разберем критические параметры:

    * --partitions (Партиции): Как мы помним из первой статьи, это единица параллелизма. Если вы укажете 1 партицию, вы сможете читать этот топик только одним консюмером в группе. Если укажете 50, сможете запустить 50 параллельных консюмеров. > Совет: Начинайте с разумного минимума (например, 3 или 6), так как партиции потребляют ресурсы (файловые дескрипторы и память контроллера). * --replication-factor (Фактор репликации): Определяет надежность. Значение 1 означает отсутствие отказоустойчивости. Значение 3 — промышленный стандарт (выдерживает падение до 2 брокеров).

    !Визуализация распределения новых партиций по брокерам после выполнения команды создания топика.

    Изменение конфигурации (Alter)

    Требования бизнеса меняются. Вчера нам нужно было хранить логи 7 дней, сегодня — 30 дней. Вчера хватало 3 партиций, сегодня нагрузка выросла в 10 раз.

    Изменение количества партиций

    Вы можете увеличить количество партиций в существующем топике:

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

    Кроме того, вспомните формулу распределения по ключу из предыдущей статьи:

    Где — номер партиции, — хеш ключа, — количество партиций, а — операция взятия остатка от деления.

    Если вы измените (количество партиций) с 3 на 6, то результат формулы для того же самого ключа изменится. Сообщения с user_id=123, которые раньше летели в Партицию 0, теперь могут полететь в Партицию 4. Это нарушит гарантию порядка сообщений для этого пользователя. Поэтому изменение количества партиций для топиков с ключами — крайне болезненная операция.

    Динамическая конфигурация (Dynamic Configs)

    Многие параметры можно менять без перезагрузки брокеров, используя kafka-configs.sh. Например, изменим срок хранения данных (retention) для конкретного топика на 1 час (3600000 мс):

    Эти настройки сохраняются в ZooKeeper (или KRaft metadata log) и имеют приоритет над глобальными настройками в server.properties.

    Мониторинг: На что смотреть?

    Kafka — надежная система, но «молчаливые» проблемы могут привести к катастрофе. Существует три главных метрики, за которыми нужно следить 24/7.

    1. Consumer Lag (Лаг консюмера)

    Это самая важная метрика для приложений. Лаг — это разница между последним сообщением, записанным в партицию (Log End Offset), и последним сообщением, которое обработал консюмер (Current Offset).

    Где — лаг, — оффсет последнего сообщения в логе, — текущий оффсет консюмера.

    * Если лаг растет, значит, ваши консюмеры медленнее, чем продюсеры. * Если лаг стабилен, но велик — у вас высокая задержка (latency), но система справляется.

    !Графическое представление лага как разницы между скоростью записи и скоростью чтения.

    2. Under Replicated Partitions (URP)

    Это главная метрика здоровья кластера. URP показывает количество партиций, у которых число синхронизированных реплик (ISR) меньше, чем заданный фактор репликации.

    Если Replication Factor = 3, а в ISR находится только 2 реплики, партиция считается under-replicated.

    Причины: * Один из брокеров упал. * Сеть перегружена, и фолловеры не успевают копировать данные. * Диск на брокере работает слишком медленно.

    > Ненулевое значение URP — это сигнал тревоги. Это означает, что надежность ваших данных снижена.

    3. Active Controller Count

    В кластере Kafka всегда должен быть ровно один брокер-контроллер, который управляет выбором лидеров.

    * Если значение равно 0: Кластер неуправляем. * Если значение больше 1: Ситуация «split-brain» (раскол мозга), два брокера думают, что они главные. Это может привести к потере данных.

    UI-инструменты для мониторинга

    Хотя CLI удобен для скриптов, смотреть на графики приятнее в веб-интерфейсе. Стандартного UI в Kafka нет, но индустрия использует популярные open-source решения:

  • Kafka-UI (ранее UI for Apache Kafka): Легковесный, удобный интерфейс для просмотра топиков, сообщений и изменения настроек.
  • AKHQ (ранее KafkaHQ): Мощный инструмент с возможностью просмотра содержимого Avro/Protobuf сообщений.
  • Confluent Control Center: Платное энтерпрайз-решение с глубокой аналитикой.
  • Резюме курса

    В этой серии статей мы разобрали архитектуру Kafka от байтов на диске до администрирования кластера:

  • Мы узнали, что Топик — это набор Партиций, а партиции состоят из Сегментов.
  • Мы поняли, что Репликация и механизм ISR гарантируют сохранность данных при падении серверов.
  • Мы изучили политики очистки: Delete (по времени) и Compact (по ключу).
  • Мы настроили Продюсеров и Консюмеров, разобравшись в семантике доставки.
  • И наконец, научились управлять топиками и следить за метриками Lag и URP.
  • Apache Kafka — это сложный, но невероятно мощный инструмент. Понимание этих основ позволит вам строить масштабируемые и надежные потоковые системы.