Сериализация и Schema Registry

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

Schema Registry — сервис, хранящий схемы сообщений (Avro, Protobuf, JSON Schema) и проверяющий их совместимость при изменениях, чтобы эволюция формата не ломала потребителей.

Зачем это нужно

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

Бинарные форматы со схемой

Avro и Protobuf сериализуют данные по схеме: схема описывает поля и типы, а на проводе летит компактный бинарный код, а не многословный JSON с повторяющимися именами полей. Это и меньше байт, и строгая типизация.

{
  "type": "record",
  "name": "Order",
  "fields": [
    {"name": "order_id", "type": "string"},
    {"name": "amount", "type": "double"},
    {"name": "currency", "type": "string", "default": "RUB"}
  ]
}

Как работает Registry

  Продюсер -> регистрирует/находит схему в Registry -> получает schema id
  В сообщение пишется: [magic][schema id][бинарные данные]
  Консьюмер -> читает schema id -> тянет схему из Registry -> декодирует

В сам топик летит не схема, а её короткий id. Консьюмер по id получает точную схему, которой данные были закодированы, и корректно их разбирает — даже если у него «своя» версия схемы.

Эволюция схем и совместимость

Тип совместимостиПравило
BACKWARDновый консьюмер читает старые данные (можно удалять поля / добавлять с default)
FORWARDстарый консьюмер читает новые данные (можно добавлять поля)
FULLи то, и другое одновременно

Registry при регистрации новой версии схемы проверяет её совместимость со старой по выбранному правилу и отклоняет несовместимое изменение. Так ломающее изменение не попадёт в прод: его завернут на этапе деплоя продюсера.

Как работает под капотом

Поле с default — ключ к совместимости: если новый консьюмер встречает старое сообщение без поля currency, он подставит "RUB" из схемы; если старый консьюмер встречает новое поле, которого не знает, он его проигнорирует. Avro кодирует данные позиционно по схеме, без имён полей в каждом сообщении, поэтому совпадение схем критично — именно schema id на проводе связывает байты с правильной схемой. Registry хранит версии схем под «subject» (обычно имя-топика-value) и ведёт их историю.

Частые ошибки

  • Сырой JSON без схемы в прод-топиках. Любое изменение формата — мина под потребителей.
  • Добавить поле без default. Ломает BACKWARD-совместимость: старые данные не прочитать новой схемой.
  • Менять тип поля. Смена int -> string почти всегда несовместима; вводите новое поле.

Итоги

  • Схема — это контракт данных; Avro/Protobuf дают компактность и строгую типизацию.
  • В топик пишется schema id, по нему консьюмер берёт точную схему из Registry.
  • Registry проверяет совместимость (BACKWARD/FORWARD/FULL) и отклоняет ломающие изменения.
Проверьте себя
1. Что летит в топик при использовании Avro + Schema Registry?
AПолная схема в каждом сообщении
BКороткий schema id плюс бинарные данные
CТолько JSON
DИмя топика
2. Что значит BACKWARD-совместимость?
AСтарый продюсер пишет новые данные
BНовый консьюмер может читать старые данные (например, поля добавляются с default)
CДанные нельзя менять
DСхема удаляется
3. Почему добавление поля без default ломает совместимость?
AУвеличивает размер
BСтарые сообщения не содержат поля, и новая схема без default не знает, чем его заполнить
CAvro не поддерживает поля
DRegistry запрещает любые поля