gRPC in ASP.NET Core 3.0 -- Protocol Buffer(1)

現現在微服務很流行,而微服務頗有多是使用不一樣語言進行構建的。而微服務之間一般須要相互通訊,因此微服務之間必須在如下幾個方面達成共識:git

  • 須要使用某種API
  • 數據格式
  • 錯誤的模式
  • 負載均衡
  • 。。。

如今最流行的一種API風格多是REST,它主要是經過HTTP協議來傳輸JSON數據。github

可是如今咱們能夠看看gRPC(https://grpc.io/),它來自Google,而且支持衆多主流的語言包括Go,Dart,C#,C/C++,Nodejs,Python等等。數組

下面就學習一下gRPC。服務器

 

gRPC能解決哪些問題?

構建(Web)API是挺麻煩的,由於構建API時咱們得考慮:網絡

  • 數據的格式是JSON、XML仍是二進制的;
  • 端點地址以及GET仍是POST等;
  • 如何調用API以及對異常的處理規則;
  • API的效率:一次調用讀取多少數據?是否太多了或太少了?太少的話可能會致使屢次API的調用;
  • 延遲;
  • 擴展性,是否能支持成上千個客戶端
  • 負載均衡
  • 與其餘語言的互操做性
  • 如何處理身份認證、監控、日誌等等

以上這些問題聽說gRPC都能解決。。😱負載均衡

 

再次介紹一下gRPC

以前說了gRPC來自Google,它是一個開源的框架;它同時也是Cloud Native Computation基金會(CNCF)的一部分,就像Docker和Kubernetes同樣。框架

gRPC容許你爲RPC(Remote Procedure Call)定義請求和響應,而後gRPC會幫你處理一切剩餘問題函數

它速度快,執行效率高,基於HTTP/2構建,低延遲,支持流,與開發語言無關,而且能夠很簡單的插入身份認證、負載均衡、日誌和監控等功能。微服務

 

RPC是啥

RPC是(Remote Procedure Call)遠程過程調用。學習

在客戶端代碼使用RPC調用的時候,就像直接調用了服務端的一個函數同樣。

例如在服務器端代碼是這樣的:

 

而在「遙遠」的客戶端它是這樣調用服務器端的邏輯的,就像調用本地方法同樣:

而實際上客戶端在調用這個方法的時候,是要走網絡通訊的。

 

RPC它不是一個新的概念,很早它就出現了。可是它存在不少的問題。而gRPC它是對RPC一種很是簡潔的實現而且解決了不少RPC的問題。

 

 

如何學習gRPC?

首先,你得學習Protocol Buffers(https://developers.google.com/protocol-buffers/),簡單的說,它能夠用來定義消息和服務。

而後,你只須要實現服務便可,剩餘的gRPC代碼將會自動爲你生成。

.proto這個文件能夠適用於十幾種開發語言(包括服務端和客戶端),而且它容許你使用同一個框架來支持每秒百萬級以上的RPC調用。

 

gPRC使用的是合約優先的API開發模式,它默認使用Protocol buffers (protobuf) 做爲接口設計語言(IDL),這個.proto文件包括兩部分:

  • gRPC服務的定義
  • 服務端和客戶端之間傳遞的消息

看一個官網的例子(protobuf):

在這裏定義了一個Greeter服務,它裏面定義了一個SayHello的rpc調用。SayHello會發送HelloRequest這個消息,接收HelloReply這個消息。

 

爲何使用Protocol Buffers?

由於:

  • 它和開發語言無關
  • 能夠生成全部主流開發語言的代碼
  • 數據是二進制格式的,串行化的效率高,Payload比較小
  • 也很適合傳遞大量的數據
  • 經過設定某些規則,是的API的進化也很簡單

 

Protocol Buffer

開發環境:

  • IDE: VSCode
  • VSCode的擴展插件:vscode-proto3和Clang-Format這兩個擴展
  • Windows還須要安裝Clang,Windows 64位系統的地址以下:Clang for Windows (64-bit);Mac:brew install clang-format 。

 

第一個例子

選個文件夾,創建一個名叫first.proto的文件:

1. 這行代碼表示咱們使用的是語法是proto3,以前還有一個proto2;若是你不寫這一行,那麼protocol buffer編譯器會認爲你採用的是proto2。這個必須是文件的第一個非空非註釋行。

2. 這裏是定義了一個消息名稱爲FirstMessage,類型是message。它裏面定義了三個字段,它們都是標量類型(Scalar Type),你也能夠定義複合類型,這個之後再說。

3. 是指字段(Field)的類型

4. 字段的名稱

5. 字段的數值(也叫Tag),這個數字是惟一的。它們是用來在信息格式裏識別你的字段的,一旦該類型被使用了,那麼這個數字就不要再改變了。

 

標量類型

數值型

數值型有不少種形式:double, float, int32, int64, uint32, uint64, sint32, sint64, fixed32, fixed64, sfixed32, sfixed64。

根據須要選擇對應的數值類型。

 

布爾型

bool型能夠有True和False兩個值。

 

字符串

string表示任意長度的文本,可是它必須包含的是UTF-8編碼或7位ASCII的文本,長度不可超過232

 

字節型

bytes可表示任意的byte數組序列,可是長度也不能夠超過232  ,最後是由你來決定如何解釋這些bytes。例如你可使用這個類型來表示一個圖片。

 

作個例子

能夠本身作一個例子,需求是這樣的:這個信息表示的是一我的Person,使用proto3語法,字段以下:ID,姓名,身高,體重,頭像,電子郵件,郵件是否已驗證。

這個應該沒有什麼難度,不過要注意一下別忘記標點符號。

 

字段的數值(Tag)

在Protocol Buffers裏面,字段的名其實沒那麼重要,可是寫C#代碼的時候,字段名仍是很重要的。

對於protobuf來講,這個tag是更爲重要的。

可使用的最小的tag數值是1,最大值是229 - 1, 或者 536,870,911。可是你不可使用19000到19999之間的數,這部分數是保留的。

還有一點值得注意的是:

從1到15的Tag數只佔用1個字節的空間,因此它們應該被用在頻繁使用的字段上。而從16到2047,則佔用兩個字節,它們能夠用在不頻繁使用的字段上。

 

字段規則

protobuf的字段必須知足如下兩個規則之一:

單數字段(Singular)

大概意思就是指這個字段只能出現0或1次(不能超過一次),這也是proto3的默認字段規則。

 

重複字段(Repeated)

與singular相對的就是repeated。若是你想作一個list或數組的話,你可使用重複字段這個概念。這個list能夠有任何數量(包括0)的元素。它裏面的值的順序將會獲得保留。

 

Repeated Fields 例子

仍是使用前面的Person這個例子,咱們在裏面添加一個repeated字段(電話號碼):

就是在前面加上repeated這個關鍵字便可。

在proto3裏面,標量類型的repeated字段採用的是packed編碼。

 

註釋

proto文件裏能夠添加註釋。它們一般被看成你定義的這些消息的文檔。

註釋很簡單,仍是兩種形式,直接看代碼就明白了:

 

保留的字段

若是你對你定義的消息類型進行了更新,例如刪除某個字段或者註釋掉某個字段,那麼其它開發者在之後更新這個消息類型的時候可能會從新使用被你刪除/註釋掉的字段的數值(tag)。若是之後還須要使用這個消息類型的老版本的proto文件,那麼這將會引發嚴重的問題,例如數據損壞、隱私漏洞等等。

那麼一種避免此類事情發生的解決辦法就是將你刪除/註釋掉的這些字段的數值(或/而且包括字段名,由於字段名可引發JSON序列化的問題)標記爲reserved,若是其餘人再使用這個數值做爲字段標識符,那麼編譯器就會有錯誤提示:

注意,不能夠把reserved數值和字段名放在同一個reserved語句裏。

 

字段的默認值

當消息被解析的時候,若是編碼的消息裏不含有特定的一個singular元素,那麼在被解析對象裏相應的字段就會被設爲默認值。

經常使用類型的默認值以下:

  • string:空字符串
  • bytes:空的byte數組
  • bool:false
  • 數值型:0
  • 枚舉enum:枚舉裏定義的第一個枚舉值,值必須是0
  • repeated:一般是相應開發語言裏的空list
  • 還有個消息類型的字段,它的默認值和開發語言有關,這個之後再說。

 

枚舉

以前說了,枚舉裏面定義的第一個值就是這個枚舉的默認值。

Enum的tag必須從0開始,因此0就是枚舉的數值默認值。

 

繼續上個例子

咱們對Person添加一個枚舉類型的字段:性別 Gender:

首先須要定義枚舉類型,這裏定義了一個枚舉,名稱是Gender,裏面有3個值,默認值是NOT_SPECIFIED,數值默認值就是0。

而後使用這個枚舉類型定義了一個字段,名稱爲gender,tag數爲10。

 

爲枚舉值起別名

枚舉值是能夠起別名的,起別名的做用就是容許兩個枚舉值擁有同一個數值。

要想起別名,首先須要設置allow_alias這個option爲true:

而後咱們爲FEMALE這個枚舉值起了一個別名叫作WOMAN,它們的數值是同樣的。一樣的MAN是MALE的數值也是同樣的。

 

枚舉裏面的常量的值必須不能超過32位整型的數值,不建議使用負數。

枚舉能夠定義在message裏面,也能夠在外邊單獨定義以便複用。若是另外一個消息想使用Person裏面這個Gender枚舉,那麼可使用Person.Gender這種形式。

 

針對枚舉值被刪除/註釋掉這種狀況,它也可使用reserved:

數值和常量名也必須分開使用兩個reserved語句。

其中max表示可能的最大的值。

 

使用其它的信息類型

可使用其它的信息類型做爲字段的類型。

咱們能夠在同一個proto文件裏定義多個信息類型(爲了截圖方便,我去掉了Person的一些字段):

在這個文件裏,除了Person信息類型外,我還定義了Date信息類型。

因此,我能夠在Person裏面使用Date做爲它的字段類型:

 

引入定義

若是想要使用的信息類型已經在其它的proto文件定義好了呢?這個時候就須要引入信息類型的定義。

如今我把Date定義移動到了date.proto這個文件裏面:

而後在person.proto裏面咱們能夠引用date.proto:

 

嵌套類型

Protocol Buffer容許在信息類型裏面定義其它的信息類型。

直接看例子:

若是想在Person外邊使用Address這個類型,那麼就須要這樣用:Person.Address。

 

打包

你能夠向proto文件添加可選的打包(package)說明符,以免消息類型間的名稱衝突。

因此說打包是很必要的。

打包以後生成的C#代碼就會使用命名空間來對應proto裏面的package,可是命名方式會改成Pascal Case(每一個單詞首字母大寫)。

上面的代碼在C#裏面的狀況就是:Person類在My.Project這個命名空間下。

可是若是你在proto文件裏設置了option csharp_namespace這個選項,那麼在C#裏的命名空間就是該選項指定的命名空間了:

這時候,C#裏面Perosn類的命名空間就是My.WebApis了,可是在proto文件裏它的包仍是my.project。

 

設置Protocol Buffers編譯器

protoc編譯器主要就是用來生成代碼的,它的下載地址目前是:https://github.com/protocolbuffers/protobuf/releases/

在裏面選擇你使用的操做系統的版本:

下載後解壓縮到某個路徑,而後把解壓目錄下的bin目錄添加到系統的環境變量裏。

而後打開命令行,輸入protoc,若是有相似下面的東西出現,說明安裝成功了:

這裏面的--proto_path=PATH這個參數比較經常使用,它用來指定到哪一個文件見來查找引入。

再有就這個參數很經常使用:

--csharp_out=OUT_DIR用來指定存放生成的C#代碼的目錄。

 

咱們先試驗一下,生成Person的C#代碼:

執行成功後就沒有任何提示,打開csharp目錄,能夠看到Person.cs這個文件:

而Person.cs文件裏面的代碼就比較多了:

千萬不要去修改這個文件!

 

第一篇文章先到這。

相關文章
相關標籤/搜索