Синтаксис Protobuf. Определение сообщений и сервиса

Последнее обновление: 24.11.2022

Для определения файла proto, который определяет формат сервиса и сообщений, применяется специальный язык Protobuf. Protobuf имеет свою систему типов данных и опреления сообщений. Вкратце рассмотрим синтаксис файла proto и сопоставление его некоторых элементов с элементами языка C#.

Базовые типы proto

Protobuf поддерживает использование многих стандартных примитивных типов, которые применяются в ряде наиболее популярных языков программирования:

Тип proto

Тип C#

double

double

float

float

int32 (только для положительных чисел)

int

int64 (только для положительных чисел)

long

uint32

uint

uint64

ulong

sint32

int

sint64

long

fixed32

uint

fixed64

ulong

sfixed32

int

sfixed64

long

bool

bool

string

string

bytes (произвольная последовательность байтов длиной не более 232)

ByteString

Типы int32 и int64 применяются преимущественно для положительных чисел, а для отрицательных лучше применять соответственно sint32 и sint64.

При парсинге сообщения если для какого-то поля сообщения не отсутствует значение, то это поле получает значение по умолчанию: для строк - пустая строка, для чисел - 0, для bool - false.

Определение сообщений

Для передачи данных в Protobuf используются сообщения. В языке C# им соответствуют классы. Определение сообщения начинается с ключевого слова message, за которым следует имя сообщения.

message имя_сообщения { }

Далее внутри фигурных скобок помещаются поля сообщения. Поля определяются в формате:

тип имя = значение;

Например, определим сообщение Person с двумя полями name и age:

message Person{
	string name = 1;
	int32 age = 2;
}

В качестве значения каждому полю передается уникальный номер для идентификации каждого поля при сериализации сообщения. Собственно поэтому каждое поле в рамках сообщения должно иметь уникальное числовое значение. Причем при использовании чисел от 1 до 15 в качестве значения в бинаром представлении в сообщение добавляется дополнительный байт. Значения от 16 до 2047 добавляют два дополнительных байта. Поэтому для наиболее часто используемых в сообщении данных лучше указывать значения от 1 до 15. Допустимые значения: от 1 до 536870911 (з исключением диапазона чисел от 19000 до 19999).

По этому сообщению в C# будет создаваться класс Person c двумя свойствами Age и Name. Если упрощенно, он будет соответствовать примерно следующему классу:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

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

Стоит отметить, что стайлгайд для Protobuf рекомендует использовать для именования полей так называемый "snake_case" (змеиный_регистр), при котором в составном имени составные части разделяются прочерком, например, "first_name". И Microsoft также рекомендует следовать этому стилю наименования, поскольку при генерации соответствующих типов C# будут автоматически применяться стандарты именования .NET. Например, для поля protobuf first_name в C# формируется свойство FirstName.

Nullable-типы

Для определения полей, которые допускают значение null, (например, для свойства с типом int? в коде C#), в Protobuf достпны типы-обертки:

Тип C#

Тип Protobuf

bool?

google.protobuf.BoolValue

double?

google.protobuf.DoubleValue

float?

google.protobuf.FloatValue

int?

google.protobuf.Int32Value

long?

google.protobuf.Int64Value

uint?

google.protobuf.UInt32Value

ulong?

google.protobuf.UInt64Value

string?

google.protobuf.StringValue

ByteString

google.protobuf.BytesValue

Для их использования в файле .proto необходимо импортировать файл wrappers.proto:

import "google/protobuf/wrappers.proto";

message Person{
	google.protobuf.StringValue name = 1;
	google.protobuf.Int32Value age = 2;
}

Определение сервиса

Сервис определяется с помощью ключевого слова service, после которого идет имя сервиса:

service Greeeter {
}

Тело сервиса составляют функции, которые определяются с помощью ключевого слова rpc

rpc имя_функции (входящее_сообщение) returns (отправляемое_сообщение);

После rpc идет имя_функции, а затем в скобках тип сообщения, которое получает сервис. Далее после оператора returns - тип сообщения, которое возвращает сервис.

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
  string name = 1;
}
message HelloReply {
  string message = 1;
}

Сервис может содержать множество функций, но стоит учитывать, что она обязательно должно принимать какое-то сообщение и возвращать какое-нибудь сообщения. Но при необходимости можно создавать также пустые сообщения

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
  rpc Test (VoidRequest) returns (TestResponse);
}
message HelloRequest {
  string name = 1;
}
message HelloReply {
  string message = 1;
}

// пустое сообщение запроса
message VoidRequest{ }

message TestResponse{
	string text = 1;
}

Типы методов

Сервисы gRPC может определять различные типы методов. От типа методов зависит, как сервис будет получать и отправлять сообщения. Поддерживаются следующие типы методов:

  • Унарные (сервис отправляет и получает обычное сообщение)

  • Потоковая передача сервера (сервер получает обычное сообщение, а отправляет поток)

  • Потоковая передача клиента (сервер получает поток данных, а отправляет обычное сообщение

  • Двунаправленная потоковая передача (сервер получает поток данных и отправляет поток данных)

Потоки определяются с помощью ключевого слова stream, которое ставится перед названием сообщения:

syntax = "proto3";

service ExampleService {
  // унарный метод
  rpc UnaryCall (ExampleRequest) returns (ExampleResponse);

  // Потоковая передача сервера
  rpc StreamingFromServer (ExampleRequest) returns (stream ExampleResponse);

  // Потоковая передача клиента
  rpc StreamingFromClient (stream ExampleRequest) returns (ExampleResponse);

  // Двунаправленняя потоковая передача
  rpc StreamingBothWays (stream ExampleRequest) returns (stream ExampleResponse);
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850