【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.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?=?"jdoe@example.com",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_namespaceusing?(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
長按二維碼關注總結
以上是生活随笔為你收集整理的【gRPC】ProtoBuf 语言快速学习指南的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C# 9 新特性 —— 补充篇
- 下一篇: 孙丕恕离开浪潮 仪器厂历时60年成为服务