Protobuf и JSON Schema
Schema Registry поддерживает три формата сериализации: Avro, Protocol Buffers и JSON Schema. В Уроке 02 мы подробно разобрали Avro. В этом уроке разберём два альтернативных формата — их преимущества, ограничения и правила эволюции — а затем сравним все три.
Protocol Buffers в Schema Registry
Protocol Buffers (Protobuf) — бинарный формат сериализации от Google, исходно разработанный для межсервисного взаимодействия (gRPC). Схема описывается в .proto файлах.
syntax = "proto3";
package com.example.kafka;
message Order {
int32 id = 1;
string product = 2;
double amount = 3;
int64 timestamp_millis = 4;
optional string metadata = 5;
}
Ключевая особенность Protobuf: каждое поле имеет номер поля (field number), который кодируется в бинарный payload вместо имени поля. Это делает бинарное представление максимально компактным и — что критически важно для совместимости — независимым от имён полей.
Schema Registry хранит .proto файл как текст. При регистрации указывается schemaType: "PROTOBUF".
Преимущества Protobuf
Наименьший размер бинарного payload для сложных вложенных структур. Protobuf использует varint-кодирование для чисел и не хранит имена полей — только номера. Для сообщения с большим числом числовых полей Protobuf компактнее Avro.
Мощная экосистема инструментов. protoc — компилятор схем, поддерживает генерацию кода для 10+ языков. Плагины для gRPC, REST, JSON mapping. Первоклассная поддержка в Bazel, Maven, Gradle.
Первоклассная gRPC-интеграция. Если вы строите микросервисы на gRPC, Protobuf — естественный выбор. Kafka-события могут использовать те же .proto определения, что и gRPC-сервисы, без преобразования схем.
Поддержка oneof, map, вложенных message. Богатая система типов с семантикой из коробки.
Правила эволюции Protobuf
Совместимость в Protobuf строится на нескольких строгих правилах:
Никогда не переиспользуйте номера полей. Если поле 5: string metadata было удалено, номер 5 нельзя использовать для нового поля. Старые клиенты могут прочитать байты под номером 5 с неправильной интерпретацией типа.
Используйте reserved для удалённых полей:
message Order {
reserved 5;
reserved "metadata"; // запрещает случайное переиспользование имени
int32 id = 1;
string product = 2;
double amount = 3;
int64 timestamp_millis = 4;
// поле 5 удалено
}
Добавление новых полей всегда совместимо при правильной нумерации. В proto3 все поля опциональны по умолчанию. Старые клиенты игнорируют неизвестные номера полей (unknown fields preservation).
Изменение типа опасно. Protobuf позволяет изменить тип только внутри совместимых wire types (например, int32 → int64 — wire type 0 в обоих случаях). Изменение между несовместимыми wire types ломает декодирование.
oneof нельзя добавить к существующему полю. Если поле string metadata = 5 существует, нельзя сделать его частью oneof. Нужно удалить и создать новую структуру с новым номером.
JSON Schema в Schema Registry
JSON Schema описывает структуру JSON-документа в стандарте Draft 7 (Confluent Schema Registry 7.9.x использует JSON Schema Draft 7 или 2019-09).
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Order",
"type": "object",
"properties": {
"id": {"type": "integer"},
"product": {"type": "string"},
"amount": {"type": "number"},
"timestamp_millis": {"type": "integer"},
"metadata": {"type": ["string", "null"]}
},
"required": ["id", "product", "amount"]
}
При регистрации: "schemaType": "JSON". Payload — обычный JSON-текст, без бинарного кодирования. Wire format тот же: magic byte + schema ID + JSON bytes.
Преимущества и ограничения JSON Schema
Преимущества:
Человекочитаемый payload. Сообщения можно читать и отлаживать без инструментов десериализации. kafka-console-consumer показывает читаемый JSON.
Нулевой порог входа. Разработчик, знакомый с JSON, понимает JSON Schema без дополнительного обучения. Идеально для web-native команд.
Нет кодогенерации. JSON Schema валидирует данные на лету — не нужны предварительно сгенерированные классы.
Ограничения:
Нет бинарного кодирования. Каждое сообщение — это JSON-текст. При высоком throughput это значительно хуже Avro и Protobuf по size и CPU.
Слабые гарантии эволюции. JSON Schema не имеет формального механизма совместимости уровня Avro reader/writer schema resolution. Confluent Schema Registry реализует ограниченный набор проверок совместимости для JSON Schema, но они менее строгие.
additionalProperties: false блокирует расширение. Если схема запрещает дополнительные поля, любое добавление нового поля в producer будет отклонено на стороне потребителя при валидации.
Если вы не уверены, какой формат выбрать — используйте Avro. Это стандарт де-факто для Kafka и Schema Registry с лучшей поддержкой инструментов, самым строгим контролем совместимости и зрелой экосистемой.
Сравнение трёх форматов
| Характеристика | Avro | Protobuf | JSON Schema |
|---|---|---|---|
| Кодирование | Бинарное | Бинарное | Текст (JSON) |
| Схема в payload | Нет (только ID) | Нет (только ID) | Нет (только ID) |
| Гарантии совместимости | Строгие | Строгие | Слабые |
| Размер сообщения | Малый | Наименьший | Большой |
| Экосистема | Kafka-native | gRPC-native | Web-native |
| Инструменты | avro-tools, GenericRecord | protoc, generated classes | jsonschema validators |
| Кодогенерация | Опциональная (SpecificRecord) | Обязательная | Не нужна |
| Читаемость payload | Нет (бинарный) | Нет (бинарный) | Да |
| Нулевой порог входа | Средний | Высокий | Низкий |
Когда выбирать каждый формат
Avro — для Kafka-first систем. Наилучшая интеграция со Schema Registry, строжайший контроль совместимости, компактный размер, зрелые Java-библиотеки. Выбор по умолчанию для любого нового Kafka-проекта без специфических требований.
Protobuf — для gRPC-heavy микросервисов. Если ваши сервисы общаются по gRPC с теми же данными, которые публикуются в Kafka, использование одних и тех же .proto файлов устраняет дублирование схем. Наименьший размер payload при сложных вложенных структурах.
JSON Schema — для отладки и web-native интеграций. Когда читаемость payload важнее эффективности. Для интеграций с внешними партнёрами, где JSON является общим языком. Для команд без Java/Scala опыта, где кодогенерация является барьером.