簡介
gRPC 是一款高性能、開源的 RPC 框架,產自 Google,基於 ProtoBuf 序列化協議進行開發,支持多種語言(Golang、Python、Java等),本篇只介紹 Python 的 gRPC 使用。因爲 gRPC 對 HTTP/2 協議的支持使其在 Android、IOS 等客戶端後端服務的開發領域具有良好的前景。gRPC 提供了一種簡單的方法來定義服務,同時客戶端可以充分利用 HTTP/2 stream 的特性,從而有助於節省帶寬、降低 TCP 的連接次數、節省CPU的使用等
gRPC 默認使用 protocol buffers,這是 Google 開源的一種輕便高效的結構化數據存儲格式,可以用於結構化數據串行化,或者說序列化。它很適合做數據存儲或 RPC 數據交換格式。
安裝
- gRPC 的安裝:
$ pip install grpcio
- 安裝 ProtoBuf 相關的 python 依賴庫:
$ pip install protobuf
- 安裝 python grpc 的 protobuf 編譯工具:
$ pip install grpcio-tools
實踐
定義一個消息類型
先來看一個非常簡單的例子。假設你想定義一個“搜索請求”的消息格式,每一個請求含有一個查詢字符串、你感興趣的查詢結果所在的頁數,以及每一頁多少條查詢結果。可以採用如下的方式來定義消息類型的.proto文件了:
syntax = "proto3"; // 聲明使用 proto3 語法
message SearchRequest {
string query = 1; // 每個字段都要指定數據類型
int32 page_number = 2; // 這裏的數字2 是標識符,最小的標識號可以從1開始,最大到2^29 - 1, or 536,870,911。不可以使用其中的[19000-19999]
int32 result_per_page = 3; // 這裏是註釋,使用 //
}
- 文章的第一行指定了你正在使用 proto3 語法:如果不指定,編譯器會使用 proto2。這個指定語法必須是文件的非空非註釋的第一行。
- SearchRequest消息格式有三個字段,在消息中承載的數據分別對應於每一個字段。其中每個字段都有一個名字和一種類型。
- 向.proto文件添加註釋,可以使用C/C++/java風格的雙斜槓(//) 語法格式。
- 在消息體中,每個字段都有唯一的一個數字標識符。這些標識符用來在消息的二進制格式中識別各個字段,一旦開始使用就不能再改變。
[1,15]之內的標識號在編碼的時候會佔用一個字節。[16,2047]之內的標識號則佔用2個字節。所以應該爲那些頻繁出現的消息元素保留 [1,15]之內的標識號。切記:要爲將來有可能添加的、頻繁出現的標識號預留一些標識號。
指定字段規則
所指定的消息字段修飾符必須是如下之一:
- singular:一個格式良好的消息應該有0個或者1個這種字段(但是不能超過1個)。
- repeated:在一個格式良好的消息中,這種字段可以重複任意多次(包括0次)。重複的值的順序會被保留。
在proto3中,repeated的標量域默認情況蝦使用packed。
數值類型
一個標量消息字段可以含有一個如下的類型——該表格展示了定義於.proto文件中的類型,以及與之對應的、在自動生成的訪問類中定義的類型:
默認值
當一個消息被解析的時候,如果被編碼的信息不包含一個特定的singular元素,被解析的對象鎖對應的域被設置位一個默認值,對於不同類型指定如下:
- 對於strings,默認是一個空string
- 對於bytes,默認是一個空的bytes
- 對於bools,默認是false
- 對於數值類型,默認是0
- 對於枚舉,默認是第一個定義的枚舉值,必須爲0;
- 對於消息類型(message),域沒有被設置,確切的消息是根據語言確定的,詳見generated code guide
- 對於可重複域的默認值是空(通常情況下是對應語言中空列表)。
嵌套類型
你可以在其他消息類型中定義、使用消息類型,在下面的例子中,Result消息就定義在SearchResponse消息內,如:
message SearchResponse {
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
repeated Result results = 1;
}
在 message SearchResponse 中,定義了嵌套消息 Result,並用來定義SearchResponse消息中的results域。
Protobuf 文件編譯
從.proto文件生成了什麼?
當用protocol buffer編譯器來運行.proto文件時,編譯器將生成所選擇語言的代碼,這些代碼可以操作在.proto文件中定義的消息類型,包括獲取、設置字段值,將消息序列化到一個輸出流中,以及從一個輸入流中解析消息。
- 對C++來說,編譯器會爲每個.proto文件生成一個.h文件和一個.cc文件,.proto文件中的每一個消息有一個對應的類。
- 對Java來說,編譯器爲每一個消息類型生成了一個.java文件,以及一個特殊的Builder類(該類是用來創建消息類接口的)。
- 對Python來說,有點不太一樣——Python編譯器爲.proto文件中的每個消息類型生成一個含有靜態描述符的模塊,,該模塊與一個元類(metaclass)在運行時(runtime)被用來創建所需的Python數據訪問類。
- 對go來說,編譯器會位每個消息類型生成了一個.pd.go文件。
- 對於Ruby來說,編譯器會爲每個消息類型生成了一個.rb文件。
- javaNano來說,編譯器輸出類似域java但是沒有Builder類
- 對於Objective-C來說,編譯器會爲每個消息類型生成了一個pbobjc.h文件和pbobjcm文件,.proto文件中的每一個消息有一個對應的類。
- 對於C#來說,編譯器會爲每個消息類型生成了一個.cs文件,.proto文件中的每一個消息有一個對應的類。
Python gRPC 示例
編譯
使用以下命令編譯:
$ python -m grpc_tools.protoc -I./ --python_out=. --grpc_python_out=. ./hello.proto
生成了兩個文件:
- hello_pb2.py 此文件包含生成的 request(HelloRequest) 和 response(HelloReply) 類。
- hello_pb2_grpc.py 此文件包含生成的 客戶端(GreeterStub)和服務端(GreeterServicer)的類。
創建服務端代碼
創建和運行 Greeter 服務可以分爲兩個部分:
-
實現我們服務定義的生成的服務接口:做我們的服務的實際的“工作”的函數。
- 運行一個 gRPC 服務器,監聽來自客戶端的請求並傳輸服務的響應。
創建客戶端代碼
運行代碼
-
首先運行服務端代碼
python server/main.py
-
然後運行客戶端代碼
python client/main.py
輸出:
\>>>Greeter client received: Hello, goodspeed!