python中RPC使用(gRPC)

簡單介紹

遠程過程調用(英語:Remote Procedure Call,縮寫爲 RPC,也叫遠程程序調用)是一個計算機通信協議。該協議允許運行於一臺計算機的程序調用另一臺計算機的子程序,而程序員無需額外地爲這個交互作用編程。如果涉及的軟件採用面向對象編程,那麼遠程過程調用亦可稱作遠程調用或遠程方法調用。

gRPC是由Google公司開源的高性能RPC框架。支持多語言、多平臺,其消息協議使用Google自家開源的Protocol Buffers協議機制(proto3) 序列化,傳輸使用HTTP/2標準,支持雙向流和連接多路複用。

使用方法

安裝

pip install grpc
pip install grpcio-tools

如果你使用的是pycharm,需要安裝插件protobuf support

使用 Protocol Buffers(proto3)的IDL接口定義語言定義接口服務,編寫在文本文件(以.proto爲後綴名)中

 

 Protocol Buffers

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

注:在gRPC中推薦使用proto3版本。

文檔結構

1. 版本

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的方法的。

數據類型

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

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,但因爲枚舉值使用變長編碼,所以不推薦使用負數作爲枚舉值,因爲這會帶來效率問題。

消息類型

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

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

 1. 字段編號

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

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

2. 指定字段規則

消息字段可以是以下之一:

  • singular:格式良好的消息可以包含該字段中的零個或一個(但不超過一個)。
  • repeated:此字段可以在格式良好的消息中重複任意次數(包括零)。將保留重複值的順序。對應Python的列表。
 message Result {
    string url = 1;
    string title = 2;
    repeated string snippets = 3;
  }

 3. 添加更多消息類型

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

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

message SearchResponse {
 ...
}

 4. 保留字段

保留變量不被使用

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

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

 5. 默認值

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

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

6. 嵌套類型

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

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

如果要在其父消息類型之外重用此消息類型,使用

SearchResponse.Result

 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中,使用類型的默認值。

 oneof

如果你的消息中有很多可選字段, 並且同時至多一個字段會被設置, 你可以加強這個行爲,使用oneof特性節省內存。

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

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

然後你可以增加oneof字段到 oneof 定義中. 你可以增加任意類型的字段, 但是不能使用repeated 關鍵字。

定義服務

Protocol Buffers使用service定義RPC服務。

注:一個service中可定義多個方法。

message HelloRequest {
  string greeting = 1;
}

message HelloResponse {
  string reply = 1;
}

service HelloService {
  rpc SayHello (HelloRequest) returns (HelloResponse) {}
}

 編譯生成代碼

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

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

  • reco_pb2.py 保存根據接口定義文件中的數據類型生成的python類
  • reco_pb2_grpc.py 保存根據接口定義文件中的服務方法類型生成的python調用RPC方法

定義消息的生成 XxxStub 類,客戶端可以使用它來調用Xxx RPC

定義服務的生成XxxServer類,定義了服務的實現接口

編寫服務器和客戶端程序

生成文件需要下載,pycharm中在當前目錄中右鍵 Deployment -> Download from Copy ...

# 使用with語句連接rpc服務器
with grpc.insecure_channel('127.0.0.1:8888') as channel:
    pass

更多內容訪問官方文檔:https://grpc.io/docs/

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章