天天看點

gRPC in ASP.NET Core 3.x -- Protocol Buffer(3)更新消息類型添加字段對字段重命名删除字段預設值枚舉

當你第一次定義Protocol Buffer的消息的時候,你肯定會給消息設定一套規則需求。但是随着時間的推進,你的業務可能會發生了變化,與此同時,你的Protocol Buffer消息類型的需求也會随之變化。

也就是說:有一些字段可能會發生變化,可能會添加一些字段,也可能會删除一些字段。但是可能有很多程式正在使用/讀取你的Protocol Buffer的消息,但是它們沒法都随着需求進行更新。是以,在你對源資料進行演進的時候,一定不要引起破壞性變化,否則其它的程式可能就無法正常工作了。

主要有這兩種情景:

  1. 向前相容變更:使用新的.proto檔案來寫資料 --- 從舊的.proto檔案讀取資料
  2. 向後相容變更:使用舊的.proto檔案來寫資料 --- 從新的.proto檔案讀取資料

有時候這兩種情況同時存在,也就是全相容變更。

為了達到此目的,Protocol Buffer制定了一些更新消息類型的規則:

  1. 不要修改任何現有字段的數字(tag)
  2. 你可以添加新的字段,那些使用舊的消息格式的代碼仍然可以将消息序列化,您應該注意這些元素的預設值,以便新代碼可以與舊代碼生成的消息正确互動。類似的,新代碼所建立的消息也可以被舊代碼解析:舊的二進制在解析的時候會忽略新的字段。
  3. 字段可以被删除,隻要它們的數字(tag)在更新後的消息類型中不再使用即可。你也可以把字段名改為使用“OBSOLETE_”字首而不是删除字段,或者把這些字段的數字(tag)進行保留(reserved),以免未來其它開發者不消息使用了删除字段的數字。
  4. 對于資料類型的變化,例如int32到int64,string到bytes等等,可以參考官方文檔:

    https://developers.google.com/protocol-buffers/docs/proto3#updating。 但是建議還是盡量不要去修改字段的資料類型。

添加字段

原來的proto是這樣的:

gRPC in ASP.NET Core 3.x -- Protocol Buffer(3)更新消息類型添加字段對字段重命名删除字段預設值枚舉

然後我添加一個name字段:

gRPC in ASP.NET Core 3.x -- Protocol Buffer(3)更新消息類型添加字段對字段重命名删除字段預設值枚舉

而這時,如果把新的消息發送到舊的代碼的時候,舊代碼不知道2這個數字tag對應的是什麼,是以name這個字段就會被忽略掉。

反過來,如果我們使用新的代碼讀取舊的資料,那麼就會找不到新的字段,這時候就會使用該字段類型的預設值(空字元串)。

是以,處理預設值的時候一定要非常的小心。

對字段重命名

現在我把name這個字段的名改成了full_name,而它的數字不變:

gRPC in ASP.NET Core 3.x -- Protocol Buffer(3)更新消息類型添加字段對字段重命名删除字段預設值枚舉

這樣做是沒有任何問題的。

你可以随意改變字段的名字,隻要它的數字tag不變就行,因為Protocol Buffer裡面這個數字tag才是最重要的。

删除字段

現在我又把full_name字段删除了:

gRPC in ASP.NET Core 3.x -- Protocol Buffer(3)更新消息類型添加字段對字段重命名删除字段預設值枚舉

這時候,如果舊的代碼找不到這個字段了,那麼就會采用預設值。

反過來,如果我們使用新的代碼讀取舊的資料,那麼已删除的字段将會被忽略/丢棄。

但是,在删除字段的時候,你應該一直都保留字段的數字tag以及字段名,像這樣:

gRPC in ASP.NET Core 3.x -- Protocol Buffer(3)更新消息類型添加字段對字段重命名删除字段預設值枚舉

這樣做是防止數字tag和名稱被重複使用,避免在以後的代碼庫裡造成沖突。

使用OBSOLETE

之前說了,可以把字段名改為 OBSOLETE_字段名 來代替删除字段,但是這樣做的缺點就是:你還是需要把這個字段的值計算出來。我還是建議使用reserve的方式進行删除字段的管理。

Reserved

  • 你可以保留字段的數字tag和字段名;
  • 但是不可以在同一行語句裡混合reserved數字tag和字段名,應該分成兩個語句:
gRPC in ASP.NET Core 3.x -- Protocol Buffer(3)更新消息類型添加字段對字段重命名删除字段預設值枚舉
  • 保留字段數字tag的目的就是防止數字tag被重複使用;
  • 而保留字段名的目的就是防止出現一些程式bug;

注意:一定不要移除reserved的數字tags。

預設值

預設值在更新Protocol Buffer消息定義的時候有很重要的作用,它可以防止對現有代碼/新代碼造成破壞性影響。它們也可以保證字段永遠不會有null值。

但是,預設值還是非常危險的:

  • 你無法區分這個預設值到底是來自一個丢失的字段還是字段的實際值正好等于預設值。

那麼應該怎麼辦?

  • 需要保證這個預設值對于業務來說是一個毫無意義的值。例如 int32 pop(人口)預設值就可以設定為-1。
  • 再就是,可能需要在你的代碼裡來做一些對預設值的判斷,進而進行處理。

枚舉

enum同樣可以進化,就和消息的字段一樣,可以添加、删除值,也可以保留值。

但是如果代碼不知道它接收到的值對應哪個enum值,那麼enum的預設值将會被采用。

例如這個enum:

gRPC in ASP.NET Core 3.x -- Protocol Buffer(3)更新消息類型添加字段對字段重命名删除字段預設值枚舉

如果程式代碼接收到了5這個數值,那麼它找不到對應的枚舉值,是以就會使用這個枚舉的預設值0(UNSPECIFIED)。