Статьи

Протокол Buffers основные вещи, которые вы должны знать

Protocol Buffers — это двоичный протокол, разработанный в Google и сделанный общедоступным. Первой общедоступной версией была Protocol Buffers version 2. Самая последняя реализация на момент написания этой статьи — Protocol Buffers version 3. Версия 1 никогда не была общедоступной.

Буферы протокола — это независимый от языка, платформенно-независимый, расширяемый механизм Google для сериализации структурированных данных. Буферы протокола в настоящее время поддерживают сгенерированный код в Java, Python, Objective-C, C ++, C #, JS и других. Вы определяете, как вы хотите, чтобы ваши данные были структурированы один раз, затем вы можете использовать специальный сгенерированный исходный код, чтобы легко записывать и считывать ваши структурированные данные в различные потоки данных и из них, используя различные языки.

Причины смотреть на буфер протокола

Вы можете спросить себя, зачем мне вообще рассматривать или рассматривать буферы протокола, поэтому давайте рассмотрим несколько причин.

Размер данных

Тот факт, что Protobuf является двоичным протоколом, дает ему очень хорошие характеристики. По умолчанию его пропускная способность намного выше по сравнению с текстовыми протоколами, например, JSON и XML. Это не должно быть сюрпризом. Однако, например, разница в пропускной способности между буферами протокола и JSON может быть уменьшена путем сжатия данных при отправке с использованием текстовых протоколов, и можно использовать некоторые другие приемы. Если это будет сделано, буферные протоколы будут по-прежнему лучше пропускной способности, но не с таким большим запасом, поэтому я советую всегда делать это, если вы используете текстовые форматы для передачи данных.

Загрузка процессора

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

Если вы думаете, что, возможно, это не так уж и важно, подумайте на секунду о вашем сценарии использования. Сколько данных вы передаете и кто потребляет и производит ваши данные. Если вы размещаете свои серверы в каком-либо облаке и оплачиваете их процессором, это может повлиять на ваш счет. Мобильные устройства не такие мощные, как наши компьютеры с процессором, и любая интенсивная загрузка процессора приведет к разрядке их батарей. В обоих случаях двоичный протокол Protobuf является хорошим выбором для повышения производительности и удобства работы пользователей.

использование

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

В случае протокольных буферов все начинается со схемы или, если быть более точным, прото-файл. Если этот файл протока используется как часть gRPC, то есть плагины maven и gradle для автоматической генерации классов и тому подобное для нас. В случае, если буферы протокола будут использоваться в качестве автономных, мы можем сгенерировать все классы и аналогичные вручную, используя компилятор буферов протокола.

Начальный файл прото

Давайте посмотрим на простой файл прото

1
2
3
4
5
6
syntax = "proto3";
 
package xyz.itshark.blog.protobuf;
 
option java_package = "xyz.itshark.blog.protobuf.generated";
option java_multiple_files = true;

Здесь мы определяем синтаксис как proto3 — Буферы протокола версии 3.

Во второй строке мы говорим, что все сгенерированные классы и остальные должны быть частью пакета «xyz.itshark.blog.protobuf».

Следующие две строки добавляют определенное поведение в случае, если мы генерируем код Java. В случае кода Java мы будем использовать другой пакет «xyz.itshark.blog.protobuf.generated», также мы говорим, что мы хотим, чтобы было создано несколько файлов вместо одного класса, содержащего все классы, определенные в файле прото.

Первое сообщение

Давайте добавим это в наш файл прото

1
2
3
4
5
message Example {
     int32 id = 1;
     string first_name = 2;
     string last_name = 3;
}

Это простой пример сообщения в protobuf. Его имя — Пример, и оно состоит из трех полей: id, first_name и last_name. Если вы знаете C или C ++, это может выглядеть очень похоже на struct.

Первое поле имеет тип int32 и имя идентификатора . Кроме того, в конце есть что-то странное: знак «=» и номер 1 . Все поля имеют это, только разные номера. Эти числа являются «тегами» и используются для позиционирования значений внутри двоичного сообщения для более быстрой и простой сериализации и десериализации данных. Очень важно помнить, что если какое-то число используется в каком-либо сообщении, оно никогда не может быть изменено или использовано повторно. Если, например, через некоторое время мы решим, что не хотим иметь идентификатор в нашем сообщении, мы можем изменить определение сообщения, чтобы оно выглядело следующим образом

1
2
3
4
5
message Example {
     string first_name = 2;
     string last_name = 3;
     Int32 new_id = 4;
}

Тем не менее, мы не можем сделать что-то подобное

1
2
3
4
5
message Example {
     string not_valid = 1;
     string first_name = 2;
     string last_name = 3;
}

Так как он будет выдавать разные сообщения с одинаковыми тегами и завершит работу любого приложения, которое все еще использует старый файл proto.

Второе поле в нашем примере сообщения имеет тип string и имеет имя first_name . Если вы из мира Java, вы заметите, что это не верблюжий случай, а правильный способ именования данных в Java. Это стандартный способ именования полей в буферах протокола. Компилятор буферов протокола проанализирует файл прото и сгенерирует код с соответствующим синтаксисом для языка под рукой. Не забывайте, что protobuf не зависит от языка и используется в разных языках, которые имеют разную синтаксическую логику для именования классов, атрибутов и тому подобного.

Все поля в примере сообщения являются необязательными. Чтобы быть более точным, все поля в случае протокола версии 3 являются необязательными. В случае версии 2 необходимо было явно пометить поля как необязательные или как обязательные. Проблема с обязательными полями заключалась в обратной совместимости и том факте, что они никогда не могли быть удалены. Проанализировав множество вариантов использования, было решено, что обязательные поля приносят больше вреда, чем пользы в долгосрочной перспективе, и в версии 3 все поля стали необязательными по умолчанию.

Генерация исходного кода

Вам нужно скачать компилятор protobuf для вашей системы по этому адресу https://github.com/google/protobuf/releases , как только вы установите его в своей системе, просто запустите эту команду, и вы получите сгенерированный код Java, готовый для использования в вашем проект

1
$ protoc example.proto --java_out=~\code\blog\

Расширенное сообщение

Давайте посмотрим на немного более сложное сообщение сейчас

Список / Массивы

Вы можете спросить себя, как определить список или массив элементов внутри сообщения. Для этого нужно просто добавить повтор перед именем поля

1
2
3
message Advanced {
 repeated string text = 4;
}

Enum

Многие языки программирования имеют перечисления, вы также можете определить их в буферах протокола. Для определения enum вам просто нужно добавить что-то вроде этого

1
2
3
4
5
enum Status {
SUCCESS = 0;
            FAIL = 1;
            RANDOM = 2;
}

После этого вы можете использовать его как любой другой тип

1
2
3
4
message Advanced {
 repeated string text = 4;
             Status my_status = 3;
}

Сообщение в сообщении

Вы можете добавлять сообщения также внутри других сообщений. Например, в нашем случае мы определили сообщение Example, поэтому мы можем добавить поле в сообщение Advanced типа Example

1
2
3
4
5
message Advanced {
 repeated string text = 4;
         Status my_status = 3;
         Example message_example = 5;
}

Вы можете определить сообщение внутри сообщения и таким образом заставить его использовать «личное», только внутри этого сообщения.

01
02
03
04
05
06
07
08
09
10
11
message Example2 {
message Internal {
       string text = 1;
        }
 
Internal valid_message = 1;
}
 
Message Invalid {
           Internal can_not_be_used_here = 1;
}

Первое сообщение из этого примера (Example2) является действительным сообщением, в то время как второе сообщение (Invalid) недопустимо, как указано его именем, так как Internal может использоваться только внутри Example2.

Полный файл прото

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
syntax = "proto3";
 
package xyz.itshark.blog.protobuf;
 
option java_package = "xyz.itshark.blog.protobuf.generated";
option java_multiple_files = true;
 
enum Status {
    SUCCESS = 0;
    FAIL = 1;
    RANDOM = 2;
}
 
message Example {
    int32 id = 1;
    string first_name = 2;
    string last_name = 3;
}
 
message Advanced {
    repeated string text = 4;
    Status my_status = 3;
    Example message_example = 5;
}
 
message Example2 {
message Internal {
string text = 1;
        }
 
Internal valid_message = 1;
}

Использование буферов протокола в коде Java

Как только у нас есть готовый файл прото и мы сгенерируем код Java, следующим шагом будет использование его в сочетании с нашим кодом. Если вы использовали protoc для генерации кода, вы можете просто скопировать его в соответствующее место в вашем проекте. Как только это будет сделано, вы можете создать подобный код для создания экземпляра объекта Example.

1
2
3
4
5
Example example = Example.newBuilder()
                .setId(1)
                .setFirstName("First")
                .setLastName("Last")
                .build();

Как вы можете видеть, мы используем компоновщики для генерации экземпляров сообщений Protocol Buffers. Для генерации расширенного сообщения мы можем использовать такой код:

1
2
3
4
5
6
Advanced advanced = Advanced.newBuilder()
                .setMyStatus(Status.RANDOM)
                .addText("some text")
                .addText("other text")
                .setMessageExample(example)
                .build();

И это все, что нужно сделать.

Ресурсы

Опубликовано на Java Code Geeks с разрешения Владимира Деяновича, партнера нашей программы JCG . Смотрите оригинальную статью здесь: Protocol Buffers основные вещи, которые вы должны знать

Мнения, высказанные участниками Java Code Geeks, являются их собственными.