天天看點

【gRPC】ProtoBuf 語言快速學習指南

繼上篇【gRPC】 在.Net core中使用gRPC了解了gRPC的使用,gRPC基于

HTTP/2

ProtoBuf

ProtoBuf

就非常有必要好好了解一下了,

那麼

ProtoBuf

究竟是什麼?

ProtoBuf =Google Protocol Buffer

是一種語言無關、平台無關、可擴充的序列化結構資料的方法,它可用于(資料)通信協定、資料存儲等。

是一種靈活,高效,自動化機制的結構資料序列化方法-可類比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更為簡單。

可以定義資料的結構,然後使用特殊生成的源代碼輕松的在各種資料流中使用各種語言進行編寫和讀取結構資料。你甚至可以更新資料結構,而不破壞由舊資料結構編譯的已部署程式。

ProtoBuf 是一種資料表達方式,google又說它是資料交換格式,交換 ,也就是說着眼點在資料的傳輸上。

在資料表達方式上,可以類比json或者xml,但是不同于 json 可以直接被讀取解析,需要

  • 1.建立.proto檔案,定義資料結構:維護一套對象協定
  • 2.protoc編譯.proto檔案生成讀寫接口
  • 3.調用接口實作序列化、反序列化以及讀寫

gRPC誕生于2015年,而ProtoBuf 最早從2001年開始就在谷歌内部使用了,後者強調的就是簡單和性能,在谷歌内部廣泛運用于存儲和交換各種結構化資訊,前者強調的是通信。

1. Message

systax="proto"

message SearchRequest {
	string query = 1;
  	int32 page_number = 2;
  	int32 result_per_page = 3;
}
           
  • syntax

    :使用的文法,如果不指定,編譯器就會按

    proto2

    編譯。檔案第一行必須是非空,非注釋。
  • message

    :

    SearchRequest

    定義了3個字段

1.1 指定字段類型

你可以指定字段為标量類型,string,int32,當然也可以指定複合類型,包括枚舉等其他一些類型。然後通過protocol buffer編譯器去生成不同語言平台的代碼。官方給出了相關的proto标量類型與不同語言平台類型映射表。

類型預設值

string>empty string

bytes>empty bytes

bool>false

數字類型>0

enums>定義的第一個枚舉值0

枚舉類型

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
  enum Corpus {
    UNIVERSAL = 0;
    WEB = 1;
    IMAGES = 2;
    LOCAL = 3;
    NEWS = 4;
    PRODUCTS = 5;
    VIDEO = 6;
  }
  Corpus corpus = 4;
}
           

每個枚舉類型必須包含一個常量,第一個常量必須是0,

message MyMessage1 {
  enum EnumAllowingAlias {
    option allow_alias = true;
    UNKNOWN = 0;
    STARTED = 1;
    RUNNING = 1;
  }
}
           

allow_alias設定為true,就可以将相同的值配置設定給不同的枚舉常量。

字段為消息

message SearchResponse {
  repeated Result results = 1;
}

message Result {
  string url = 1;
  string title = 2;
  repeated string snippets = 3;
}
           

ps:

repeated

可以用來存放N個相同類型的内容

導入定義

嵌套消息,在目前*.proto檔案就如上使用。如果Result在其他.proto檔案呢?

import "myproject/other_protos.proto";
           

字段為嵌套類型

message SearchResponse {
  message Result {
    string url = 1;
    string title = 2;
    repeated string snippets = 3;
  }
  repeated Result results = 1;
}
           

1.2 配置設定字段編号

每個字段都有一個獨一無二的編号。這些編号作用就大了,因為消息是二進制格式,這些編号就是用來辨別消息中的字段,這個可以類比一些通信協定中的編碼格式。

  • 1-15需要1個位元組編碼
  • 16-2047需要兩個位元組

官方提示,頻繁出現的消息元素定義至1-15,将來可能頻繁出現的消息元素也要在1-15為其留點位置。

1.3 多個消息類型

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

message SearchResponse {
 ...
}
           

1.4 注釋

/* SearchRequest represents a search query, with pagination options to
 * indicate which results to include in the response. */

message SearchRequest {
  string query = 1;
  int32 page_number = 2;  // Which page number do we want?
  int32 result_per_page = 3;  // Number of results to return per page.
}
           

1.5 保留字段

如果通過完全删除字段或注釋來更新消息類型。如果以後加載相同.proto的舊版本,可能會導緻嚴重問題,包括資料損壞、隐私漏洞等。

比如删除了編号1 的字段,修改為其他字段,服務端已更新,用戶端還是舊版本,用戶端和服務端的編号為1的字段不一緻。

確定不會發生這種情況的一種方法是指定保留已删除字段的字段号。如果将來有任何使用者試圖使用這些字段辨別符,協定緩沖區編譯器将會提示。文法如下:

message Foo {
  reserved 2, 15, 9 to 11;
  reserved "foo", "bar";
}

           

1.6 Protocol buffer 編譯器

編譯器會根據選擇的語言平台生成相應的代碼

2.Services

消息類型定義完成後,便是我們使用gRPC的重頭戲,Service=RPC(Remote Procedure Call).在proto檔案中定義RPC service接口,編譯器就會根據你選擇的語言平台存根生成服務接口代碼。看如下示例:

service SearchService {
  rpc Search (SearchRequest) returns (SearchResponse);
}
message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
  enum Corpus {
    UNIVERSAL = 0;
    WEB = 1;
    IMAGES = 2;
    LOCAL = 3;
    NEWS = 4;
    PRODUCTS = 5;
    VIDEO = 6;
  }
  Corpus corpus = 4;
}
message SearchResponse {
  message Result {
    string url = 1;
    string title = 2;
    repeated string snippets = 3;
  }
  repeated Result results = 1;
}
           
  • service

    -關鍵字
  • SearchService

    -服務名
  • Search

    -方法名
  • SearchRequest

    -傳入的參數
  • SearchResponse

    -傳回的參數

3.Packages

您可以向.proto檔案添加一個包說明符,當然這個Packages是可選,主要是為了防止message 之間的命名沖突。

package foo.bar;
message Open { ... }
           

在C#中,除非在.proto檔案中顯式地指明選項

csharp_namespace

,否則包名就會在轉換為PascalCase格式後,作為名稱空間。更多其他語言參考官方文檔說明。

4.Options

4.1 檔案級别

頂級,不在任何消息,枚舉或者服務的定義

option csharp_namespace = "GrpcService";

package greet;
           

這個就是生成代碼時命名空間(java就是包嘛),如果不指定csharp_namespace,如上描述,命名空間就會取package的名稱:greet。

4.2 消息級别

僅在消息定義内部

4.3 字段級别

僅在字段定義内部

4.4 類型級别

枚舉類型,枚舉值,服務類型,服務方法,但是目前這個級别的還沒啥用,可能未來為了湧現的新需求會開始發揮作用。

更多詳情,示例用法,參考官方

5.編譯

在.Net Core 3.0中,在上面的幾個關鍵部分書寫完成,基本上就能針對proto檔案進行自動編譯生成服務端或用戶端代碼,隻需要進行各自的開發即可,這如絲般順滑的體驗,當然是微軟為開發者行的友善,但是我們還是有必要了解一下刀耕火種的方式(僅僅是了解,有槍可以用,誰還去拼刺刀,刀快還是槍快?):

5.1 下載下傳編譯器

https://github.com/protocolbuffers/protobuf/releases/tag/v3.12.2

win64包

5.2 編譯

protoc --proto_path=IMPORT_PATH --csharp_out=DST_DIR path/to/file.proto
           
  • --proto_path

    :proto檔案的目錄,可多次指定,

    -I

    可以簡化指令
  • --cscharp_out

    :輸出C#檔案的位置,其他語言平台
    • --cpp_out

    • --java_out

    • --python_out

    • --go_out

    • --ruby_out

    • --objc_out

    • --php_out

    顧名思義,就不一一贅述
  • DST_DIR

    :可以指定為.zip,注意,如果輸出存檔已經存在,它将被覆寫;編譯器不夠智能,無法将檔案添加到現有存檔。
protoc -I=$SRC_DIR --csharp_out=$DST_DIR $SRC_DIR/*.proto
           

生成實體類,接口

protoc.exe -I="." .\greet.proto --csharp_out="./code"
           

生成grpc服務

protoc.exe -I="." .\greet.proto --csharp_out="./code"   --grpc_out="./code"  --plugin=protoc-gen-grpc=grpc_csharp_plugin.exe
--grpc_out: protoc-gen-grpc: 系統找不到指定的檔案。
           
  • --grpc_out

    csharp_out

    是輸出類似于咱們平時寫的實體類,接口,定義之類的。生成的檔案叫,額,就叫*.cs吧.

    grpc_out

    是跟服務相關,建立,調用,綁定,實作相關。生成的玩意叫xxxGrpc.cs。
  • --plugin=protoc-gen-grpc=grpc_csharp_plugin.exe

    這個就是csharp的插件。插件nuget幫我們下下來了,目錄

    %UserProfile%\.nuget\packages\grpc.tools\2.29.0\tools\windows_x64\grpc_csharp_plugin.exe

這裡通過指令刀耕火種生成xxxGrpc.cs失敗了,目前沒找到解決方案,相關Stack Overflow問題,但是并沒有解決。或許微軟官方知道原因,畢竟微軟進行了工具內建,生成無誤。有知道原因或者解決方案的,請在下方評論指出。灰常感謝~

6.序列化與反序列化

序列化

using Google.Protobuf;
Person john = new Person
{
    Id = 1234,
    Name = "John Doe",
    Email = "[email protected]",
    Phones = { new Person.Types.PhoneNumber { Number = "555-4321", Type = Person.Types.PhoneType.Home } }
};
using (var output = File.Create("john.dat"))
{
    john.WriteTo(output);
}
           

反序列化

using gen_namespace
   
using (var input = File.OpenRead("john.dat"))
{
    Person john;
    john = Person.Parser.ParseFrom(input);
}
           

參考連結

https://developers.google.com/protocol-buffers/

https://developers.google.com/protocol-buffers/docs/proto3

https://www.cnblogs.com/nmslanx/articles/8242105.html

https://www.jianshu.com/p/a24c88c0526a

https://www.cnblogs.com/makor/p/protobuf-and-grpc.html

https://en.wikipedia.org/wiki/Protocol_Buffers

https://developers.google.com/protocol-buffers/docs/csharptutorial#parsing-and-serialization

作者:Garfield

同步更新至個人部落格:http://www.randyfield.cn/

本文版權歸作者所有,未經許可禁止轉載,否則保留追究法律責任的權利,若有需要請聯系[email protected]

微信公衆号

掃描下方二維碼關注個人微信公衆号,實時擷取更多幹貨

【gRPC】ProtoBuf 語言快速學習指南

同步更新至:http://www.randyfield.cn/

出處:http://www.cnblogs.com/RandyField/

本文版權歸作者和部落格園共有,未經許可禁止轉載,否則保留追究法律責任的權利,若有需要請聯系[email protected].