Добрый день, Хабр!

Меня зовут Сергей Игнатенко, , я — девлид в поезде «Операционная платформа» Страхового Дома ВСК. Хочу сегодня рассказать об опыте использования SchemaRegistry и Avro в Kafka.

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

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

Принцип работы
Принцип работы

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

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

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

Здесь может возникнуть проблема: консюмер не сможет десериализовать сообщение. Почему? Причины могут быть разные. Например, версия объекта в продюсере изменилась, были добавлены новые поля или что-то не было учтено вовремя сериализации.

Задачи сериализации и десериализации
Задачи сериализации и десериализации

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

Но насколько эффективен этот метод? Если ошибка произошла, то, вероятно, даже повторная отправка сообщения не устранит проблему. Здесь требуется более глубокий анализ и решение на уровне кода.

Все эти сложности указывают на то, что мы находимся, если можно так выразиться, на "тёмной стороне" Kafka. Впрочем, дальше я расскажу о "светлой стороне" и о том, как можно минимизировать последствия таких ситуаций. Но прежде, чем перейти к решениям, позвольте сделать небольшое отступление и рассказать о том, что такое Apache Avro.

Что такое Apache Avro?

Apache Avro — это система для настройки вызова удалённых процедур и сериализации данных, созданная в рамках проекта Apache Hadoop в 2009 году.

Она использует JSON для определения типов данных и протоколов, а сами данные преобразует в компактный бинарный формат. Ниже представлена схема JSON-формата.

Схема JSON-формата
Схема JSON-формата

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

Apache Avro использует JSON для определения типов данных и протоколов, а сами данные преобразует в компактный бинарный формат. Позднее я подробно расскажу о структуре файлов Avro, их бинарном представлении и компонентах. Вся эта информация доступна в официальной спецификации, которая находится в открытом доступе.

Важно отметить, что Apache Avro — это не новинка. Несмотря на то, что релиз Kafka произошёл позже, Avro был выпущен ещё 1 ноября 2009 года. Однако, как ни странно, на практике я редко встречал его использование.

Если среди вас есть те, кто уже применяет Avro, буду рад узнать детали — в каком сервисе и для каких задач вы его используете.

Apache Avro — это не новинка
Apache Avro — это не новинка

Популярность Avro объясняется рядом факторов, таких как динамическая типизация, отсутствие необходимости в ручной разметке полей, поддержка версиирования схем и богатый набор структур данных.

Давайте познакомимся с типами данных в Avro:

Данные в Avro
Данные в Avro

Начнём с примитивных типов, которые включают в себя стандартные значения, такие как нулевые, булевые и числовые типы (int, long, float, double). Также здесь есть bytes — последовательность 8-битных байтов и string — строка символов Unicode.

Данные в Avro
Данные в Avro

Помимо примитивных, в Avro есть и сложные типы данных. Наиболее распространённым является record, представляющий собой запись, содержащую поля. Также существуют enums (перечисления), массивы (array), карты (maps), объединения типов (union) и фиксированные размеры полей (fixed). Например, для создания nullable-строки можно использовать объединение типов null и string.

Данные в Avro
Данные в Avro

Отдельно стоит обсудить типы данных, связанные со временем. Разработчики нередко сталкиваются с проблемами, связанными с временными метками, различиями часовых поясов и синхронизацией времени. Avro поддерживает типы date (количество дней с 1 января 1970 года), time (с точностью до милли- и микросекунд) и timestamp (количество милли- или микросекунд с начала Unix-эпохи).

Загрузка типов данных в Avro происходит аналогично логическим типам, используемым в базах данных. Например, строковые типы соответствуют varcharN, int — int32, long — big int и т.д. Типы времени также имеют свои эквиваленты с определённой точностью.

Данные в Avro
Данные в Avro

Так как спецификация Avro открыта, она может быть реализована в любом стеке разработки. На практике же чаще используются целевые стеки, такие как C#, Java, Python и JavaScript.

Целевые стеки
Целевые стеки

О применении Avro в контексте Kafka

Когда продюсер отправляет сообщение, оно проходит валидацию через schema registry, после чего данные преобразуются в бинарный формат Avro и отправляются. Консюмер, получив сообщение, также обращается к schema registry и десериализует данные, используя соответствующую схему.

Применение Avro в контексте Kafka
Применение Avro в контексте Kafka

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

Schema registry — это веб-сервер, разработанный на Java и входящий в платформу Confluent CP. Он предоставляет REST API для управления схемами: добавления новых, получения версий, проверки совместимости, удаления и других манипуляций.

Schema registry
Schema registry

Примеры сериализации и десериализации сообщений можно рассмотреть на языках C# и Java.

Примеры сериализации и десериализации сообщений
Примеры сериализации и десериализации сообщений

При создании продюсера используется метод setValueSerializer (в C#) или KafkaAvroSerializer (в Java). Эти классы отвечают за сериализацию сообщений. В случае консюмера используются аналогичные десериализаторы.

Получение сообщения
Получение сообщения

Что дальше?

Мы планируем пилотное внедрение Avro в рамках одного из сервисов поезда (по методологии SAFe) «Операционная платформа». В ходе пилота развернём schema registry в Docker и проведём замеры производительности при работе с Avro. Ожидается, что двоичный формат Avro покажет лучшую скорость по сравнению с JSON.

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

Теперь предлагаю обсудить часто задаваемый вопрос:

Вопрос: Почему выбирается Avro, если Kafka и schema registry отлично работают с Protocol Buffers?

Ответ: Protobuf действительно хорошо подходит для описания моделей API и может быть предпочтительным вариантом, если он уже используется как стандарт в компании. Суть в том, что, если у вас в реализации уже есть gRPC и вам нужно поддерживать совместимость с ним, Protocol Buffers может оказаться более удобным решением. Тем не менее, Avro также предоставляет гибкие возможности для сериализации и управления схемами через SchemaRegistry, включая версионирование. Тем не менее, при выборе сериализатора, мы не должны полагаться только на то что мы не знакомы с форматом Avro. Область информационных технологий – это область где постоянно появляется что-то новое, а каждые 5 лет появляются новые фреймворки и методологии разработки.

Здесь важно подчеркнуть, что SchemaRegistry способна работать не только с Avro, но и с JSON Schema, Protocol Buffers и другими форматами. Это универсальный инструмент, который упрощает управление схемами независимо от выбранного формата данных.

Подытоживая, необходимо отметить что использование SchemaRegistry и Avro позволяет минимизировать риски и повысить надёжность обработки данных в Kafka. Это особенно важно в сценариях, где точность и совместимость данных имеют критическое значение.

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

Комментарии (3)


  1. Arbane
    13.09.2024 13:55
    +2

    А как наличие схем влияет на производительность? Не будет это единой точкой отказа при всех возможностях отказоустойчивости Кафки?


    1. ignatenkosergey Автор
      13.09.2024 13:55

      В документации сказано, что client обращается в schema registry только один раз — при первом обращении ему необходимо получить схему. После получения схемы из message header извлекается magic byte и схема сохраняется в clients local cache. Следующее обращение к серверу schema registry произойдет только в случае изменения схемы.


      1. bibmaster
        13.09.2024 13:55

        Что за message header и magic byte?