Interested Article - Protocol Buffers

Protocol Buffers протокол сериализации (передачи) структурированных данных, предложенный Google как эффективная бинарная альтернатива текстовому формату XML . Разработчики сообщают, что Protocol Buffers проще, компактнее и быстрее, чем XML , поскольку осуществляется передача бинарных данных, оптимизированных под минимальный размер сообщения .

Общие сведения

По замыслу разработчиков, сначала должна быть описана структура данных, которая затем компилируется в классы. Вместе с классами идёт код их сериализации в компактном формате представления. Чтение и запись данных доступна в высокоуровневых языках программирования — таких как Java , C++ или Python .

В 2010 году бэкенд Twitter перешёл на Protocol Buffers. По заявлению разработчиков Twitter , база в триллион твитов на XML занимала бы десять петабайт вместо одного.

По заявлениям Google , Protocol Buffers по сравнению с XML :

  • проще;
  • от 3 до 10 раз меньше;
  • от 20 до 100 раз быстрее;
  • более однозначный;
  • позволяет создавать классы, которые в дальнейшем легче использовать программно.

Protocol Buffers не предназначен для чтения пользователем и представляет собой двоичный формат. Для десериализации данных необходим отдельный .proto -файл, в котором определяется формат сообщения.

Формат протокола

В общем виде формат представляет собой закодированную последовательность полей, состоящих из ключа и значения. В качестве ключа выступает номер, определённый для каждого поля сообщения в proto-файле. Перед каждым полем указываются совместно закодированные номер поля в формате varint и тип поля. Если в качестве типа указана строка ( string ), вложенное сообщение, повторяющееся сообщение или набор байт ( bytes ), то следом идёт размер данных в формате varint. Далее идёт значение, соответствующее полю (данные) .

Число в формате varint ( int32 и int64 ) кодируется в последовательность байт, в которой у всех байт, кроме последнего, старший бит ( MSB ) выставляется в 1. При преобразовании в стандартное представление старший бит каждого байта отбрасывается, а оставшиеся 7-битные составляющие соединяются друг с другом в обратном порядке . Формат восьмиразрядного varint был выбран для уменьшения размера пакета при передаче небольших чисел. Так, если число меньше 128, то оно будет занимать лишь 1 байт. Однако, числа близкие к максимально возможным, будут занимать больше места, чем в обычном формате. Например, максимальное значение, которое можно сохранить в 8-ми байтах, в формате varint — 10 байт. Отрицательные числа в формате varint всегда занимают наибольший размер, в зависимости от типа, поскольку старший бит у знакового числа выставлен в 1.

Проблема кодирования отрицательных чисел была решена использованием алгоритма ZigZag ( sint32 и sint64 ), суть которого сводится к переносу бита знака из старшего разряда в младший. Кодирование алгоритмом ZigZag предполагает, что положительные и отрицательные числа будут чередоваться друг с другом с увеличением закодированного значения. В таком случае чётные числа будут положительными, а нечётные — отрицательными.

Пусть value — исходное значение, а N — разрядность типа данных исходного значения, а encoded_value — закодированное алгоритмом ZigZag значение, тогда кодирование можно записать с помощью выражения на языке Си :

encoded_value = (value << 1) ^ (value >> (N - 1));

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

Декодирование выполняется более сложным способом: выполняется исключающее ИЛИ над закодированным значением, сдвинутым на 1 вправо для удаления знакового бита, и знаковым битом, полученным из закодированного числа, спроецированным на все биты через умножение на максимальное значение для N разрядов. Таким образом, знаковый бит переносится из младшего разряда в старший:

uvalue = ((encoded_value & 1) * MAX_VALUE(N)) ^ (encoded_value >> 1);

Значение MAX_VALUE(N) соответствует значению с N разрядами, заполненными единицами (например, 0xffffffff при N =32). Таким образом, умножение младшего бита, установленного в 1, на это число будет соответствовать значению −1 в знаковом типе данных. В языке Си декодирование необходимо осуществлять для значений encoded_value и uvalue беззнакового типа, а затем значение uvalue должно преобразовываться в знаковый тип, не меняя битовое представление.

Все числовые значения, кроме fixed64 , sfixed64 и double , в протоколе кодируются в формате varint.

Примеры использования

Для того чтобы определить структуру сериализуемых данных, необходимо создать .proto -файл с исходным кодом этой структуры. Ниже приведён пример .proto -файла для 2-й версии Protocol Buffers, где описывается информация о машине: марка, тип кузова, цвет, год выпуска и информация о предыдущих владельцах.

message Car {
  required string model = 1;

  enum BodyType {
    sedan = 0;
    hatchback = 1;
    SUV = 2;
  }

  required BodyType type = 2 [default = sedan];
  optional string color = 3;
  required int32 year = 4;

  message Owner {
    required string name = 1;
    required string lastName = 2; 
    required int64 driverLicense = 3;
  }

  repeated Owner previousOwner = 5;
}

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

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

Реализация

На данный момент компанией Google созданы компиляторы для языков программирования: C++ , Java , Python , Go , C# , Objective C , JavaScript . Но существует ряд проектов сторонних разработчиков, которые создали компиляторы для следующих языков программирования: Action Script , C , C#, Clojure , Common Lisp , D , Erlang , Go, Haskell , Haxe , JavaScript, Lua , Matlab , Mercury , Objective C, Swift , OCaml , Perl , PHP , Python, Ruby , Rust , Scala , Visual Basic , Delphi .

Реализация для языка Си

Чтобы использовать протокол в языке Си без сторонних библиотек необходимо либо использовать вставки на языке C++, если таковые поддерживаются используемым компилятором, либо делать обёртки над сгенерированным для C++ кодом в виде библиотек. Если подобные варианты не подходят, то известны следующие генераторы кода:

  • (нет поддержки обработки ошибок выделения памяти, но возможно использование собственных механизмов выделения памяти);
  • (оптимизирован под низкое потребление памяти);
  • (архивный проект).

Альтернативные протоколы

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

Проект (англ.) отличается тем, что не требует генерации кода при изменении схемы данных при использовании в динамически-типизируемых языках, а сама схема описывается в формате JSON .

Сравнение с Apache Thrift

Ключевыми особенностями Apache Thrift могут служить возможность передачи ассоциативных массивов, списков и множеств, а также встроенная поддержка удалённого вызова процедур .

Protocol Buffers Apache Thrift
Разработчик Google Facebook, Apache
Поддерживаемые языки C++, Dart, Go, Java, Python, Ruby, C#, Objective C, JavaScript и PHP C++, Java, JavaScript, Python, PHP, XSD, Ruby, C#, Perl, Objective C, Erlang, Smalltalk, OCaml, and Haskell
Исходящие форматы Бинарный Бинарный, JSON
Простые типы bool, 32/64-bit integers, float, double, string, byte sequence. Повторные свойства работают как списки. bool, byte, 16/32/64-bit integers, double, string, byte sequence, map<t1,t2>, list<t>, set<t>.
Константы Нет Да
Составной тип Сообщение Структура
Исключения Нет Да
Документация Хорошая Скудная
Лицензия BSD-style Apache
Расширения составных типов Да Нет

Примечания

  1. — 2023.
  2. от 11 сентября 2014 на Wayback Machine // Google Developers, April 2, 2012.
  3. . Дата обращения: 6 января 2011. 17 октября 2010 года.
  4. (англ.) . Google Developers. Дата обращения: 11 октября 2017. 5 сентября 2017 года.
  5. . Дата обращения: 22 мая 2017. 15 июля 2017 года.
  6. от 7 ноября 2015 на Wayback Machine , MirthLab LLC, 2009
  7. (англ.) . Google Developers. Дата обращения: 4 марта 2019. 10 апреля 2019 года.

Ссылки

Источник —

Same as Protocol Buffers