Буферы протокола - уникальный пронумерованный тег - уточнение?

Я использую протокольные буферы, и все работает нормально. за исключением того, что я не понимаю - зачем мне нужны пронумерованные теги в файле proto:

message SearchRequest {
  required string query = 1;
  optional int32 page_number = 2;
  optional int32 result_per_page = 3;
}

Конечно, я прочитал docs:

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

Я не понял, какая разница, если я его изменю. (Я создам новый прото и скомпилирую его - так зачем это волнует?)

В другой статье говорится, что:

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

if (version == 3) {
  ...
} else if (version > 4) {
  if (version == 5) {
    ...
  }
  ...
}

Вопрос

Это только я, или это совершенно неясно?

позвольте мне спросить об этом по-другому:

Если у меня есть файл proto, подобный вышеприведенному файлу, а затем я меняю его на:

message SearchRequest {
  required string query = 3; //reversed order
  optional int32 page_number = 2;
  optional int32 result_per_page = 1;
}

Какое это дело? Я повторно компилирую и добавляю файл (я делал это несколько раз за последнюю неделю).

что мне не хватает? можете ли вы предоставить человеко-человеческое объяснение для этих пронумерованных тегов?

Ответ 1

Пронумерованные теги используются для сопоставления полей при сериализации и десериализации данных.

Очевидно, что если вы измените схему нумерации и примените это изменение как к сериализатору, так и к десериализатору, проблем нет.

Учтите, что если вы сохранили данные с первой схемой нумерации и загрузили ее со второй, попробуйте загрузить query в result_per_page, и десериализация, скорее всего, не удастся.

Теперь, почему это полезно? Скажем, вам нужно добавить еще одно поле в свои данные, долго после того, как схема уже используется:

message SearchRequest {
  required string query = 1;
  optional int32 page_number = 2;
  optional int32 result_per_page = 3;
  optional int32 new_data = 4;
}

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

Ответ 2

Эти номера полей используются protobuf при кодировании и декодировании. Подробнее см. здесь.

Таким образом, каждое поле имеет тип провода, поэтому int32 имеет тип провода как 0, а номер вашего поля - 2, поэтому он будет закодирован как 0001 0000, т.е. 10 в шестнадцатеричном формате.

И позже, когда его декодировано, его левый сдвиг на 1, что делает его как 001 0000, а последние три lsb решает тип провода, то есть затем выводит его тип int field и rest решает, какое поле в прото, то есть 00010 является 2. Итак, поле 2 проводного типа 0 (int)