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

Обзор

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

  • высокая производительность — достигается реализацией Kafka без копирования и отсутствием необходимости перевода записей (когда брокер получает записи, они копируются в память как есть, без какого-либо преобразования);
  • возможность повторного чтения — будучи ближе к удаленному массиву записей, чем чистое решение для обмена сообщениями, потребитель может повторно читать одну и ту же запись несколько раз;
  • долговечность — один раздел можно реплицировать на несколько кластеров в установке (коэффициент репликации); учитывая, что записи не исчезают после использования (в отличие от «чистого» обмена сообщениями, такого как AMQP), существуют политики на уровне кластера, основанные на времени и размере, отвечающие за удаление старых данных.

Случаи использования

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

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

Богатая экосистема

Учитывая, что Kafka настолько успешна, она породила вокруг себя богатую экосистему:

  • официальные клиенты Apache Kafka — клиент Java, librdkafka (клиент C&C++);
  • обертки/каркасы: спринг-кафка, кваркус-кафка, альпакка-кафка, малрый коннектор;
  • Kafka-streams: библиотека Kafka Streams предоставляет простой способ выполнения типичных операций с потоками, таких как сопоставление, фильтрация, объединение, группировка, возможность повторной отправки результатов в Kafka или в другой приемник;
  • Kafka Connect: фреймворк для определения соединителей источника и приемника, который позволяет нам перемещать данные в Kafka и из него — примеры соединителей — Redis или Postgres, и можно написать пользовательские.

Топология

Установка Kafka (называемая кластером) состоит из 1+ брокеров Kafka, в которых хранятся разделы.
Одна тема Kafka состоит из 1+ разделов. Разделы могут быть реплицированы между брокерами в кластере (что дает нам повышенную надежность в случае потери брокера).

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

Тема и раздел Kafka

Тема Kafka состоит из 1+ разделов, причем разделы являются основной рабочей единицей назначения для производителей и потребителей Kafka.

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

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

  • производители добавляют записи только в конец раздела,
  • потребители могут читать записи с любого смещения благодаря seek API (который похож на любую файловую операцию seek).

Запись Кафки

Запись Kafka представляет собой кортеж из ключа (иногда используемый производителями для выбора раздела темы во время операций отправки), значения и набора заголовков.

В случае полученных записей мы также получаем информацию timestamp.

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

Запись сериализации

API Java Kafka предоставляет некоторую простую поддержку сериализации для ключей и значений (например, сериализаторы String или ByteArray).
Существуют также более сложные решения, такие как поддержка Avro или Protobuf — пользователи также могут решают написать свою реализацию (для чего как раз требуется реализовать соответствующие интерфейсы Serializer и Deserializer).

Сжатие

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

Поскольку оно периодически вызывается на стороне сервера, приложение может по-прежнему получать «старые» сообщения, которые еще не были сжаты, поэтому может потребоваться использование сжатия в качестве механизма для имитации «уже использованных записей» путем повторной отправки сообщения с тем же идентификатором. знать об этом.

Продюсер

Производитель Kafka добавляет записи в конец разделов.

Для повышения пропускной способности производитель Kafka может отправлять несколько записей в пакетах — это управляется свойствами batch.size и linger.ms.

Java API допускает явное указание целевого раздела, также возможно отложить это вычисление до экземпляра Partitioner.

В этом случае реализация Partitioner по умолчанию использует ключ записи:

  • если раздел указан явно — используйте предоставленный раздел,
  • при наличии ключа — использовать хэш(ключ) % количества разделов,
  • если нет ни раздела, ни ключа — используйте один и тот же раздел для одной партии.

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

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

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

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

  • реплика только лидера (acks = 1) — запись добавляется только в журнал лидера, при этом репликация происходит параллельно;
  • все реплики (acks = all / -1) — производитель возвращается только после того, как все необходимые реплики получили запись;
  • без подтверждения (acks = 0) — без гарантий, в данном случае производитель не получает информацию о смещении.

В случае acks=all также есть свойство брокера/темы min.insync.replicas, которое можно использовать для тонкой настройки гарантий.

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

Потребитель

Потребитель Kafka отвечает за опрос записей из разделов, которым он назначен.
Назначение разделов может быть либо явным (назначить API), либо может использовать управление группами потребителей (подписной API, с требование, чтобы потребитель указал свойство group.id).

Когда потребитель построен, мы можем указать смещение, которое должно использоваться изначально, если в Kafka ничего не хранится (см. Хранение смещения потребителя в разделе Kafka) — auto.offset.reset: для запуска чтение с начала, конца или броска.

Потребитель предоставляет простой API поиска (который очень похож на любой файловый) — это позволяет потребителю получать записи из любой позиции в разделе, допуская возможное повторное чтение одной и той же записи. Возможен поиск по определенному смещению, началу/концу раздела; потребитель также предоставляет способ преобразовать временную метку записи в смещение).
Внутренне потребитель просто использует это смещение с каждым запросом на выборку, который он отправляет восходящему потоку при запросе записей.

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

Группы потребителей

Группы потребителей — это механизм Kafka, который позволяет автоматически распределять разделы между членами группы потребителей.
Чтобы использовать эту функцию, нам нужно передать параметр group.id потребителю и использовать API подписки. В результате тематические разделы будут распределены между всеми членами группы потребителей (очевидно, что если членов больше, чем разделов, то некоторые потребители ничего не получат — тогда может быть оправдано увеличение количества разделов или уменьшение числа потребителей).

Kafka обеспечивает автоматическую балансировку, если члены группы присоединяются или умирают (это управляется внутренним процессом сердцебиения). Потребители должны периодически вызывать poll() (max.poll.interval.ms), чтобы сообщить кластеру, что они все еще живы — это может быть актуально в случае очень длительных сценариев обработки.

Стратегия назначения разделов настраивается с помощью свойства partition.assignment.strategy (деталь реализации: это означает, что все члены группы должны иметь одинаковую стратегию, так как любой из них может быть запрошен для вычисления назначения — назначения не вычисляются брокерами).

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

Следует отметить, что необязательно использовать API подписки — потребитель с идентификатором группы может также использовать назначить API, а затем использовать возможность хранилища смещения Kafka (см. ниже).

Хранилище потребительских смещений в Kafka

Потребитель, являющийся членом группы потребителей, может хранить свои смещения в Kafka (во внутренней теме __consumer_offsets). То, что хранится, фактически представляет собой кортеж (group.id, partition, offset), который можно прочитать, например. во время инициализации потребителя.

Смещение может сохраняться вручную с помощью API фиксации (например, commitSync) или может периодически сохраняться потребителем (enable.auto.commit и auto.commit.interval.ms).

Сохранение смещения влияет на семантику доставки записей:

  • по крайней мере один раз — если мы фиксируем смещение после обработки (поскольку наш потребитель может выйти из строя до фиксации),
  • не более одного раза —если мы зафиксируем смещение перед обработкой (поскольку наш потребитель может выйти из строя после фиксации, но до того, как мы выполним обработку).

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

Транзакции

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

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

Клиент администратора

Библиотека Java Kafka также предоставляет объект Admin, который можно использовать для выполнения операций управления в Kafka, таких как:

Некоторые из этих API могут потребовать, чтобы кластер работал под управлением определенной версии Kafka.

Показатели

У администратора/производителя/потребителя есть метод metrics(), который обеспечивает доступ к низкоуровневым метрикам (например, базовой скорости запросов, количеству входящих и исходящих байтов, скорости соединения).