Как буфер протокола обрабатывает управление версиями? - PullRequest
31 голосов
/ 15 декабря 2011

Как буферы протокола обрабатывают управление версиями типа?

Например, когда мне нужно со временем изменить определение типа? Как добавление и удаление полей.

Ответы [ 2 ]

23 голосов
/ 15 декабря 2011

Google разработал protobuf, чтобы быть довольно прощающим с версиями:

  • неожиданные данные либо сохраняются в виде «расширений» (что делает их безопасными в обоих направлениях), либо автоматически отбрасываются, в зависимости от реализации
  • новые поля обычно добавляются как «необязательные», что означает, что старые данные могут быть успешно загружены

однако:

  • не перенумеровывать поля - это нарушит существующие данные
  • вы не должны обычно изменять способ хранения любого заданного поля (то есть с 32-битного int с фиксированным значением на "varint")

В общем, хотя - он будет просто работать , и вам не нужно сильно беспокоиться о версии.

6 голосов
/ 07 июня 2018

Я знаю, что это старый вопрос, но я столкнулся с этим недавно.То, как я обошел это, - это использование фасадов и решений во время выполнения для сериализации.Таким образом, я могу исключить / обновить поле до нового типа, при этом старые и новые сообщения будут корректно обрабатывать его.

Я использую protobuf.net от Marc Gravell (v2.3.5) и C #, но теорияФасады подойдут для любого языка и оригинальной реализации Google для protobuf. * ​​1003 *

В моем старом классе была метка времени DateTime, которую я хотел изменить, чтобы включить «Kind» (анахронизм .NET).Добавление этого факта означало, что оно сериализовалось в 9 байтов вместо 8, что было бы серьезным изменением сериализации!

    [ProtoMember(3, Name = "Timestamp")]
    public DateTime Timestamp { get; set; }

Основой protobuf является НИКОГДА не менять прототипы!Я хотел прочитать старые сериализованные двоичные файлы, что означало «3», чтобы остаться.

Итак,

Я переименовал старое свойство и сделал его закрытым (да, он все еще может десериализоваться через отражениемагия), но мой API больше не показывает его пригодность!

    [ProtoMember(3, Name = "Timestamp-v1")]
    private DateTime __Timestamp_v1 = DateTime.MinValue;

Я создал новое свойство Timestamp с новым идентификатором прото и включил DateTime.Kind

    [ProtoMember(30002, Name = "Timestamp", DataFormat = ProtoBuf.DataFormat.WellKnown)]
    public DateTime Timestamp { get; set; }

Iдобавлен метод «AfterDeserialization» для обновления нашего нового времени, в случае старых сообщений

    [ProtoAfterDeserialization]
    private void AfterDeserialization()
    {
        //V2 Timestamp includes a "kind" - we will stop using __Timestamp - so keep it up to date
        if (__Timestamp_v1 != DateTime.MinValue)
        {
            //Assume the timestamp was in UTC - as it was...
            Timestamp = new DateTime(__Timestamp_v1.Ticks, DateTimeKind.Utc)     //This is for old messages - we'll update our V2 timestamp...
        }
    }

Теперь у меня есть старые и новые сообщения, сериализованные / десериализованные правильно, и моя метка времени теперь включает DateTime.Kind!Ничего не сломано.

Однако это означает, что ОБА поля будут во всех новых сообщениях, поступающих в будущем.Итак, последний штрих - использовать решение о сериализации во время выполнения, чтобы исключить старую метку времени (обратите внимание, что это не сработает, если используется обязательный атрибут protobuf !!!)

    bool ShouldSerialize__Timestamp_v1() 
    {
        return __Timestamp_v1 != DateTime.MinValue;
    }

И это все.У меня есть хороший модульный тест, который делает его из конца в конец, если кто-то захочет ...

Я знаю, что мой метод основан на магии .NET, но я считаю, что концепция может быть переведена на другие языки....

...