Бинарный протокол сериализации
Бинарный протокол сериализации — это формат представления структурированных данных в виде последовательности байтов, предназначенный для передачи между процессами или хранения. В отличие от текстовых протоколов (например, JSON, XML, YAML), бинарные протоколы не используют символы для разделения полей и не требуют преобразования чисел в строковое представление. Это позволяет достичь более высокой скорости обработки, меньшего размера результирующих данных и, в ряде случаев, более строгой типизации.
История
Потребность в эффективной сериализации данных возникла с развитием распределённых систем и межпроцессного взаимодействия. Ранние протоколы, такие как XDR (External Data Representation, разработан в Sun Microsystems в конце 1980-х годов), решали задачу представления данных в едином формате для передачи между машинами с разной архитектурой (big-endian/little-endian). XDR использовался в таких протоколах, как NFS и ONC RPC.
С развитием объектно-ориентированного программирования и необходимостью передачи сложных графов объектов появились протоколы, поддерживающие наследование, полиморфизм и циклические ссылки. Примером служит Java Object Serialization (встроена в JDK 1.1, 1997 год), а также CORBA/IIOP (Common Object Request Broker Architecture/Internet Inter-ORB Protocol, стандартизирован OMG в 1991 году). Эти протоколы были тесно связаны с конкретными языками программирования и платформами, что ограничивало их интероперабельность.
В середине 2000-х годов, с ростом популярности веб-сервисов и микросервисной архитектуры, возникла потребность в лёгких, быстрых и кроссплатформенных протоколах. Это привело к созданию Protocol Buffers (Google, 2008, открыт в 2008 году), Apache Thrift (Facebook, 2007, открыт в 2007 году) и Apache Avro (разработан в рамках Hadoop, 2009). Эти протоколы используют бинарные форматы, но при этом генерируют код для множества языков программирования, что делает их удобными для гетерогенных систем.
Классификация
Бинарные протоколы сериализации можно классифицировать по нескольким признакам.
По способу описания схемы данных
- Самодокументируемые (self-describing): Протокол, в котором данные содержат метаинформацию о своей структуре (имена полей, типы). Примеры: BSON (Binary JSON), Apache Avro (в режиме без схемы), CBOR (Concise Binary Object Representation). Недостаток — больший размер данных.
- Со схемой (schema-based): Структура данных описывается отдельно (в файле .proto, .thrift, .avsc). Данные содержат только значения полей, а их типы и порядок известны из схемы. Примеры: Protocol Buffers, Apache Thrift, Apache Avro (в режиме со схемой). Преимущество — минимальный размер и высокая скорость.
По способу кодирования чисел
- Фиксированная длина: Числа занимают строго определённое количество байт (например, int32 — 4 байта, int64 — 8 байт). Простота, но неэффективно для малых значений.
- Переменная длина (Varint): Числа кодируются в переменное количество байт, где младшие 7 бит каждого байта содержат данные, а старший бит указывает, есть ли продолжение. Используется в Protocol Buffers и Thrift. Эффективно для малых положительных чисел, но может быть медленнее для больших.
- Другие: Apache Avro использует кодирование ZigZag для знаковых целых чисел, которое преобразует отрицательные числа в положительные для более эффективного сжатия Varint.
По поддерживаемым типам данных
- Простые типы: Целые числа (int8, int16, int32, int64, uint, sint), числа с плавающей точкой (float32, float64), строки, байтовые массивы, булевы значения.
- Составные типы: Списки (массивы), словари (карты), структуры (records), перечисления (enums), объединения (unions).
- Специфические типы: В некоторых протоколах есть поддержка дат, UUID, десятичных чисел, вложенных сообщений и циклических ссылок.
Принципы работы
Схема данных
Для протоколов со схемой (например, Protocol Buffers) разработчик описывает структуру данных в специальном файле (.proto). Пример:
``protobuf message Person { required string name = 1; required int32 id = 2; optional string email = 3; } ``
Каждому полю присваивается уникальный номер (field number), который используется при кодировании. Это позволяет добавлять новые поля без нарушения обратной совместимости (старые клиенты просто игнорируют неизвестные номера).
Кодирование
Процесс сериализации преобразует объект в последовательность байтов. В Protocol Buffers это выглядит следующим образом:
- Каждое поле кодируется как ключ (tag) и значение. Ключ состоит из номера поля и типа (wire type), который определяет способ кодирования значения.
- Целые числа кодируются с помощью Varint.
- Строки и байтовые массивы кодируются как длина (Varint) + данные.
- Вложенные сообщения кодируются как длина (Varint) + сериализованное сообщение.
Десериализация
Обратный процесс — восстановление объекта из байтовой последовательности. Десериализатор читает ключи, определяет тип поля по схеме и извлекает значения. Если поле не найдено в схеме, оно игнорируется (для обратной совместимости).
Сравнение с текстовыми протоколами
| Характеристика | Бинарные протоколы (Protobuf, Thrift) | Текстовые протоколы (JSON, XML) |
|---|---|---|
| Размер данных | Меньше (в 2–10 раз) | Больше (из-за символов, пробелов, кавычек) |
| Скорость сериализации | Выше (нет парсинга строк, нет преобразования чисел) | Ниже (необходим парсинг строк) |
| Читаемость для человека | Низкая (байтовый поток) | Высокая (можно прочитать глазами) |
| Интероперабельность | Требуется схема (кодогенерация) | Самоописываемый (схема не обязательна) |
| Обратная совместимость | Поддерживается (нумерация полей) | Сложнее (зависит от реализации) |
| Поддержка языков | Зависит от протокола (обычно много) | Практически все языки |
Применение
Бинарные протоколы сериализации используются в различных областях, где требуется высокая производительность и эффективность:
- Микросервисная архитектура: Для обмена данными между сервисами (RPC, gRPC). gRPC (Google Remote Procedure Call) по умолчанию использует Protocol Buffers.
- Базы данных: Внутреннее хранение данных (например, Apache Cassandra использует Thrift для некоторых операций, MongoDB использует BSON).
- Системы обмена сообщениями: Apache Kafka использует Avro для сериализации сообщений.
- Игровая индустрия: Для передачи состояния игры, синхронизации действий игроков.
- Встраиваемые системы: Где ограничены ресурсы (память, процессор).
- Сетевые протоколы: HTTP/2 и HTTP/3 используют бинарные фреймы, хотя сами заголовки могут быть текстовыми.
Примеры популярных протоколов
Protocol Buffers (protobuf)
Разработан компанией Google. Использует схему (.proto), поддерживает множество языков (C++, Java, Python, Go, Rust, C# и др.). Обеспечивает высокую скорость и малый размер. Используется в gRPC, Google Cloud, TensorFlow.
Apache Thrift
Разработан в Facebook (продукт Meta, признанной экстремистской и запрещённой в РФ). Поддерживает не только сериализацию, но и полный стек RPC. Имеет собственную систему типов и кодогенератор. Используется в Apache Cassandra, Apache Hadoop, Evernote.
Apache Avro
Разработан в рамках проекта Hadoop. Отличается тем, что схема может быть встроена в данные (самодокументируемый режим) или храниться отдельно. Использует JSON для описания схемы. Поддерживает динамическую типизацию. Используется в Apache Kafka, Apache Spark.
BSON (Binary JSON)
Разработан для MongoDB. Представляет собой бинарное представление JSON-подобных документов. Поддерживает дополнительные типы данных (Date, ObjectId, Binary). Используется в MongoDB для хранения и передачи данных.
CBOR (Concise Binary Object Representation)
Стандарт IETF (RFC 7049). Предназначен для замены JSON в средах с ограниченными ресурсами (IoT). Поддерживает все типы JSON, а также дополнительные (бинарные данные, метки времени). Не требует схемы.
Критика
- Сложность отладки: Бинарные данные невозможно прочитать без специальных инструментов. Это затрудняет отладку и мониторинг.
- Зависимость от схемы: Для работы с данными требуется актуальная версия схемы. Управление версиями схем может быть сложным в больших системах.
- Оверхеед для малых данных: Для очень маленьких сообщений (несколько байт) накладные расходы на заголовки и ключи могут быть значительными по сравнению с полезными данными.
- Безопасность: Некоторые реализации бинарных протоколов (например, Java Object Serialization) уязвимы для атак, связанных с десериализацией (выполнение произвольного кода).
Источники
- Protocol Buffers Documentation. Google Developers.
- Apache Thrift Documentation. Apache Software Foundation.
- Apache Avro Documentation. Apache Software Foundation.
- BSON Specification. MongoDB.
- RFC 7049: Concise Binary Object Representation (CBOR). IETF.
- XDR: External Data Representation Standard. RFC 4506. IETF.
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →