gRPC【RPC自定義http2.0協議傳輸】

gRPC

 

簡介

  • gRPC是由Google公司開源的高性能RPC框架。python

  • gRPC支持多語言web

    gRPC原生使用C、Java、Go進行了三種實現,而C語言實現的版本進行封裝後又支持C++、C#、Node、ObjC、 Python、Ruby、PHP等開發語言服務器

  • gRPC支持多平臺架構

    支持的平臺包括:Linux、Android、iOS、MacOS、Windows框架

  • gRPC的消息協議使用Google自家開源的Protocol Buffers協議機制(proto3) 序列化ide

  • gRPC的傳輸使用HTTP/2標準,支持雙向流和鏈接多路複用性能

架構

C語言實現的gRPC支持多語言,其架構以下ui

 

使用方法

安裝編碼

pip install grpc
install grpcio-tools pip

使用url

  1. 使用Protocol Buffers(proto3)的IDL接口定義語言定義接口服務,編寫在文本文件(以.proto爲後綴名)中。
  2. 使用protobuf編譯器生成服務器和客戶端使用的stub代碼
  3. 編寫補充服務器和客戶端邏輯代碼

 

 

Protocol Buffers

 

Protocol Buffers 是一種與語言無關,平臺無關的可擴展機制,用於序列化結構化數據。使用Protocol Buffers 能夠一次定義結構化的數據,而後可使用特殊生成的源代碼輕鬆地在各類數據流中使用各類語言編寫和讀取結構化數據。

如今有許多框架等在使用Protocol Buffers。gRPC也是基於Protocol Buffers。 Protocol Buffers 目前有2和3兩個版本號。

在gRPC中推薦使用proto3版本。

1 文檔結構

1) Protocol Buffers版本

Protocol Buffers文檔的第一行非註釋行,爲版本申明,不填寫的話默認爲版本2。

syntax = "proto3"; 或者 syntax = "proto2";

2)Package包

Protocol Buffers 能夠聲明package,來防止命名衝突。 Packages是可選的。

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

使用的時候,也要加上命名空間,

message Foo {   ...   foo.bar.Open open = 1;   ... }

注意:對於Python而言,package會被忽略處理,由於Python中的包是以文件目錄來定義的。

3)導入

Protocol Buffers 中能夠導入其它文件消息等,與Python的import相似。

import 「myproject/other_protos.proto」;

4)定義各類消息和服務

消息messge是用來定義數據的,服務service是用來gRPC的方法的。

2 註釋

Protocol Buffers 提供如下兩種註釋方式。

// 單行註釋 /* 多行註釋 多行註釋 */

3 數據類型

3.1 基本數據類型

.proto 說明 Python
double   float
float   float
int32 使用變長編碼,對負數編碼效率低, 若是你的變量多是負數,可使用sint32 int
int64 使用變長編碼,對負數編碼效率低,若是你的變量多是負數,可使用sint64 int/long
uint32 使用變長編碼 int/long
uint64 使用變長編碼 int/long
sint32 使用變長編碼,帶符號的int類型,對負數編碼比int32高效 int
sint64 使用變長編碼,帶符號的int類型,對負數編碼比int64高效 int/long
fixed32 4字節編碼, 若是變量常常大於2^{28} 的話,會比uint32高效 int
fixed64 8字節編碼, 若是變量常常大於2^{56} 的話,會比uint64高效 int/long
sfixed32 4字節編碼 int
sfixed64 8字節編碼 int/long
bool   bool
string 必須包含utf-8編碼或者7-bit ASCII text str
bytes 任意的字節序列 str

3.2 枚舉

在 Proto Buffers 中,咱們能夠定義枚舉和枚舉類型,

enum Corpus {  UNIVERSAL = 0;  WEB = 1;  IMAGES = 2;  LOCAL = 3;  NEWS = 4;  PRODUCTS = 5;  VIDEO = 6; } Corpus corpus = 4;

枚舉定義在一個消息內部或消息外部都是能夠的,若是枚舉是 定義在 message 內部,而其餘 message 又想使用,那麼能夠經過 MessageType.EnumType 的方式引用。

定義枚舉的時候,咱們要保證第一個枚舉值必須是0,枚舉值不能重複,除非使用 option allow_alias = true 選項來開啓別名。

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

枚舉值的範圍是32-bit integer,但由於枚舉值使用變長編碼,因此不推薦使用負數做爲枚舉值,由於這會帶來效率問題。

4 消息類型

Protocol Buffers使用message定義消息數據。在Protocol Buffers中使用的數據都是經過message消息數據封裝基本類型數據或其餘消息數據,對應Python中的類。

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

4.1 字段編號

消息定義中的每一個字段都有惟一的編號。這些字段編號用於以消息二進制格式標識字段,而且在使用消息類型後不該更改。 請注意,1到15範圍內的字段編號須要一個字節進行編碼,包括字段編號和字段類型16到2047範圍內的字段編號佔用兩個字節。所以,您應該爲很是頻繁出現的消息元素保留數字1到15。請記住爲未來可能添加的經常使用元素留出一些空間。

最小的標識號能夠從1開始,最大到2^29 - 1,或 536,870,911。不可使用其中的[19000-19999]的標識號, Protobuf協議實現中對這些進行了預留。若是非要在.proto文件中使用這些預留標識號,編譯時就會報警。一樣你也不能使用早期保留的標識號。

4.2 指定字段規則

消息字段能夠是如下之一:

  • singular:格式良好的消息能夠包含該字段中的零個或一個(但不超過一個)。

  • repeated:此字段能夠在格式良好的消息中重複任意次數(包括零)。將保留重複值的順序。對應Python的列表。

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

4.3 添加更多消息類型

能夠在單個.proto文件中定義多個消息類型。

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

4.4 保留字段

保留變量不被使用

若是經過徹底刪除字段或將其註釋來更新消息類型,則將來用戶能夠在對類型進行本身的更新時重用字段編號。若是之後加載相同的舊版本,這可能會致使嚴重問題,包括數據損壞,隱私錯誤等。確保不會發生這種狀況的一種方法是指定已刪除字段的字段編號(或名稱)reserved。若是未來的任何用戶嘗試使用這些字段標識符,protobuf編譯器將會報錯。

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

4.5 默認值

解析消息時,若是編碼消息不包含特定的單數元素,則解析對象中的相應字段將設置爲該字段的默認值。這些默認值是特定於類型的:

  • 對於字符串,默認值爲空字符串。
  • 對於字節,默認值爲空字節。
  • 對於bools,默認值爲false。
  • 對於數字類型,默認值爲零。
  • 對於枚舉,默認值是第一個定義的枚舉值,該值必須爲0。
  • 對於消息字段,未設置該字段。它的確切值取決於語言。
  • 重複字段的默認值爲空(一般是相應語言的空列表)。

4.6 嵌套類型

你能夠在其餘消息類型中定義、使用消息類型,在下面的例子中,Result消息就定義在SearchResponse消息內,如:

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

若是要在其父消息類型以外重用此消息類型,使用

SearchResponse.Result 

5 map映射

若是要在數據定義中建立關聯映射,Protocol Buffers提供了一種方便的語法:

map< key_type, value_type> map_field = N ;

其中key_type能夠是任何整數或字符串類型。請注意,枚舉不是有效的key_type。value_type能夠是除map映射類型外的任何類型。

例如,若是要建立項目映射,其中每條Project消息都與字符串鍵相關聯,則能夠像下面這樣定義它:

map<string, Project> projects = 3 ;
  • map的字段能夠是repeated。
  • 序列化後的順序和map迭代器的順序是不肯定的,因此你不要指望以固定順序處理map
  • 當爲.proto文件產生生成文本格式的時候,map會按照key 的順序排序,數值化的key會按照數值排序。
  • 從序列化中解析或者融合時,若是有重複的key則後一個key不會被使用,當從文本格式中解析map時,若是存在重複的key,則解析可能會失敗。
  • 若是爲映射字段提供鍵但沒有值,則字段序列化時的行爲取決於語言。在Python中,使用類型的默認值。

6 oneof

若是你的消息中有不少可選字段, 而且同時至多一個字段會被設置, 你能夠增強這個行爲,使用oneof特性節省內存。

爲了在.proto定義oneof字段, 你須要在名字前面加上oneof關鍵字, 好比下面例子的test_oneof:

message SampleMessage {   oneof test_oneof {     string name = 4;  SubMessage sub_message = 9;   } }

而後你能夠增長oneof字段到 oneof 定義中. 你能夠增長任意類型的字段, 可是不能使用repeated 關鍵字。

7 定義服務

Protocol Buffers使用service定義RPC服務。

示例:

message HelloRequest {   string greeting = 1; }  message HelloResponse {   string reply = 1; }  service HelloService {   rpc SayHello (HelloRequest) returns (HelloResponse) {} }
 
syntax = "proto3";

message UserRequest {
    string user_id=1;
    int32 channel_id=2;
    int32 article_num=3;
    int64 time_stamp=4;
}

message Track {
    string click=1;
    string collect=2;
    string share=3;
    string read=4;
}

message Article {
    int64 article_id=1;
    Track track=2;
}

message ArticleResponse {
    string exposure=1;
    int64 time_stamp=2;
    repeated Article recommends=3;
}

service UserRecommend {
    rpc user_recommend(UserRequest) returns(ArticleResponse) {}
}

  

 

代碼生成

編譯生成代碼

python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. reco.proto
  • -I表示搜索proto文件中被導入文件的目錄
  • --python_out表示保存生成Python文件的目錄,生成的文件中包含接口定義中的數據類型
  • --grpc_python_out表示保存生成Python文件的目錄,生成的文件中包含接口定義中的服務類型

在toutiao-backend/common/rpc目錄下執行上述命令,會自動生成以下兩個rpc調用輔助代碼模塊:

  • reco_pb2.py 保存根據接口定義文件中的數據類型生成的python類
  • reco_pb2_grpc.py 保存根據接口定義文件中的服務方法類型生成的python調用RPC方法
相關文章
相關標籤/搜索