1. 程式人生 > 程式設計 >如何在.NET Core中為gRPC服務設計訊息檔案(Proto)

如何在.NET Core中為gRPC服務設計訊息檔案(Proto)

如何在.NET Core中為gRPC服務設計訊息

使用協議緩衝區規範定義gRPC服務非常容易,但從需求轉換為.NET Core,然後管理服務的演變時,需要注意幾件事。

建立gRPC服務的核心是.proto檔案,該檔案以與語言無關的格式描述了該服務。使用.proto檔案,Visual Studio可以為您的服務生成基類(您只需編寫特定於業務的程式碼),或者可以生成用於可靠訪問服務的客戶端類。

.proto檔案必須符合Google的協議緩衝區規範(通常稱為ProtoBuf)。原始檔案的內容使您可以指定服務的介面。服務介面由兩部分組成:

  • 您的gRPC服務提供的方法
  • 這些方法的引數和返回值的資料結構

您可以使用Protocol Buffers規範中[1]定義的標量型別來構建這些資料結構(在ProtoBuf中稱為“訊息”)。可用的型別包括布林值,字串,位元組陣列和各種數字型別(浮點型,整數型和長型)。沒有日期或固定的十進位制型別。在接下來的專欄中,我將向您展示如何新增時間戳型別。對於小數,您可以使用float ...並伴隨著float帶來的精度損失。

如果您要開始一個新專案,則要使用自2016年以來的proto3語法。但是,您必須在.proto檔案的第一行“非空”行上明確指定proto3標準。引用規範[2]),否則將使用proto2規範解析您的.proto檔案。指定您的檔案使用proto3看起來像這樣:

syntax = "proto3";

訊息和C#類

使用proto3規範,用於客戶資訊的訊息格式可能如下所示:

message CustomerResponse {
  int32 custid = 1;
  string firstName = 2;
  strinwww.cppcns.comg lastName = 3;
  int32 age = 4;
  fixed32 creditLimit = 5;
}

等號後的數字指定訊息中欄位的位置,從位置1開始(在我的示例中,firwww.cppcns.comstName將是訊息中的第二個欄位)。這些數字在訊息中必須是唯一的(即,您不能在同一位置使用兩個欄位)。您不必按數字順序列出欄位,但是如果您這樣做的話,則可以更輕鬆地發現重複的欄位編號(儘管Visual Studio將發現任何重複的編號,並在構建應用程式時將其報告在“錯誤列表”中)。如果需要,您也可以跳過職位。此定義僅使用奇數,例如:

message CustomerResponse {
  int32 custid = 1;
  string firstName = 3;
  string lastName = 5;
}

在.NET Core中,訊息格式被轉換為類,每個欄位都成為與訊息同名的類的屬性。命名這些屬性時,.NET Core還將欄位名稱的第一個字元轉換為大寫。因此,例如,我上一個示例中的custId欄位將成為我程式碼中CustomerResponse類上的CustId屬性。

在此過程中,還得刪除欄位名稱中的所有下劃線,並且將以下字母大寫(即,Last_name欄位名稱變為LastName屬性)。

該過程還涉及將.NET型別對映到ProtoBuf型別(例如,ProtoBuf int32變為.NET int,ProtoBuf的int64變為long,fixed32變為uint),這需要向.NET Core新增一些新類。例如,ProtoBuf支援位元組陣列,其型別為位元組。名為ByteString的新.NET資料型別支援該欄位型別。要載入ByteString,請使用ByteString類的靜態CopyFrom方法,並傳遞一個位元組陣列,如下所示:

byte[] bytes = new byte[1000];
cr.Valid = ByteString.CopyFrom(bytes);

要從ByteString檢索位元組陣列,請使用物件的CopyTo方法,並傳遞要將位元組複製到的陣列和起始位置:

cr.Valid.CopyTo(bytes,0);

陣列和字典

您也可以使用【repeated】的關鍵字將集合包括在定義中(在ProtoBuf中,不是集合的欄位稱為“單數”)。如果我的客戶訊息需要一組重複的交易金額,則可以指定如下欄位:

message Customer {
   int32 id = 1;
   repeated fixed32 transactionAmounts = 4;

重複的欄位在轉換為類的屬性時,也使用新的型別:Google.Protobuf.RepeatedField。例如,我的示例將生成Google.Protobuf.RepeatedField(無符號整數)的屬性。您可以使用{}語法來初始化陣列,如下所示:

CustomerResponse cr = new CustomerResponse
            {
                CreditLimit = {10,15,100}    
            };

您可能更可能使用其各種Add方法將專案放入集合中:

cr.CreditLimit.Add(200);

您可以使用LINQ方法(例如First())或按位置訪問RepeatedField中的專案。可以正常工作,例如:

uint tranAmount = cr.CreditLimit [1];

ProtoBuf還支援稱為map的Dictionary-type集合,該集合允許您為字典的鍵和值指定型別。我的客戶訊息可能會使用“友好名稱”來跟蹤客戶的各種信用卡,以定義一個字典,該字典包含金鑰(“彼得卡”,“我的旅行卡”)和值(信用卡號)的字串):

message CustomerResponse {
  int32 custId = 1;
  map<string,string> cards = 2;

有趣的是,在Visual Studio 2019預覽版中,編輯器不會像其他型別一樣突出顯示map物件(儘管編譯得很好)。

相應的屬性將為Google.Protobuf.Collections.MapField型別,您可以通過將其Add方法傳遞給鍵和一個值來載入它,就像其他任何Dictionary一樣。

管理變更

上線後(客戶端開始使用它)更改.proto檔案相對容易。例如,您可以將具有新位置編號的欄位新增到伺服器端軟體使用的.proto檔案中,而不會打擾仍在使用該檔案的早期版本的客戶端:客戶端只是忽略未在其.proto檔案中列出的欄位。

同樣,在相反的情況下(當伺服器.proto檔案沒有客戶端的.proto欄位具有的欄位時),客戶端只會發現伺服器未傳送的屬性被設定為其預設值。順便說一句,在伺服器的.proto檔案中定義的,未在客戶端的.proto檔案中定義的欄位仍會發送到客戶端,但是.NET不能提供一種方便的方式來訪問它(至少現在還沒有)。

確實,隨著服務的發展和修改其.proto檔案,您僅應遵守兩個規則:

  • 不要更改現有欄位的位置編號
  • 不要回收職位編號(即不要用新的欄位3替換過時的欄位3)

但是,從.proto檔案生成的屬性不可為空,因此,如果未將屬性設定為值,則它mNFKBwdXxx將被設定為其預設值。這意味著數字被設定為0;數字被設定為0。將string設定為string.Empty(長度為零的字串);www.cppcns.com布林變成虛假的;ByteString屬性預設為ByteString物件,其IsEmpty屬性設定為true;並且RepeatedField和MapField屬性均預設為其對應的物件,每個物件均不包含任何專案,並且其Count屬性設定為0。

由於這種行為,存在從服務的.proto檔案中刪除欄位並且不更新所有客戶端(或者只是在伺服器上生成響應時未在物件上設定屬性)的危險。危險是客戶端無法區分未使用的欄位和已設定為其預設值的屬性之間的區別。如果將我的客戶的有效屬性設定為false,則客戶端將無法確定客戶是否無效或伺服器是否不再生成該欄位。

您可能需要考慮將屬性初始化為某個“不合理的”值(例如,數字為-1),以便客戶端可以區分設定為預設值的屬性和已刪除的欄位之間的區別。因為這對於布林值是不可能的(布林值沒有不合理的值),所以您要特別警惕刪除(甚至不再使用)布林型別的欄位。

效率和侷限性

正如我在較早的概述中[3]所討論的那樣[4],gRPC服務的功能之一是它們的訊息比基於HTTP的(RESTful)服務小得多。如果您真的想利用這種效率,請注意位置1到15僅需要一個位元組的額外開銷(即超出儲存值的資料),而位置16到2047則需要兩個位元組。將訊息格式保持在16位以下似程式設計客棧乎是個好主意。

有關將資料打包到儘可能小的空間的選擇型別方面的其他效率提示,請參閱規範中的標量型別說明[5]。

順便說一句,您不能使用以下任何一種作為欄位位置編號:負數,0、19,000到19,999(保留給ProtoBuf使用)或大於536,870,911的數字。我是否也可以建議,如果您想使用這些數字,那麼您將遇到在本專欄中我無法解決的問題。

真的。別那樣做。

以上就是如何在.NET Core中為gRPC服務設計訊息檔案(Proto)的詳細內容,更多關於.NET Core中為gRPC服務設計訊息檔案的資料請關注我們其它相關文章!