gRPC - 使用 Python 的 Hello World 應用
現在讓我們建立一個基本的“Hello World”型別的應用,它將使用 gRPC 以及 Python。
.proto 檔案
首先讓我們在 **common_proto_files** 中定義 **greeting.proto** 檔案 -
syntax = "proto3";
service Greeter {
rpc greet (ClientInput) returns (ServerOutput) {}
}
message ClientInput {
string greeting = 1;
string name = 2;
}
message ServerOutput {
string message = 1;
}
現在讓我們仔細看看上面程式碼塊中的每一行 -
syntax = "proto3";
這裡的 **"syntax"** 表示我們使用的 Protobuf 版本。因此,我們使用最新的版本 3,並且該模式因此可以使用對版本 3 有效的所有語法。
package tutorial;
這裡的 **package** 用於衝突解決,例如,如果我們有多個具有相同名稱的類/成員。
service Greeter {
rpc greet(ClientInput) returns (ServerOutput) {}
}
此塊表示服務“**Greeter**”的名稱和可以呼叫的函式名稱“**greet**”。**"greet"** 函式接收型別為 **"ClientInput"** 的輸入並返回型別為 **"ServerOutput"** 的輸出。現在讓我們看看這些型別。
message ClientInput {
string greeting = 1;
string name = 2;
}
在上面的程式碼塊中,我們定義了 **ClientInput**,它包含兩個屬性“**greeting**”和“**name**”,它們都是字串。客戶端應該將型別為“**ClientInput**”的物件傳送到伺服器。
message ServerOutput {
string message = 1;
}
在這裡,我們還定義了,給定一個“**ClientInput**”,伺服器將返回一個帶有單個屬性“**message**”的“**ServerOutput**”。伺服器應該將型別為“**ServerOutput**”的物件傳送到客戶端。
現在,讓我們為 Protobuf 類和 gRPC 類生成底層程式碼。為此,我們需要執行以下命令 -
python -m grpc_tools.protoc -I ..\common_proto_files\ -- python_out=../python --grpc_python_out=. greeting.proto
但是,請注意,要執行該命令,我們需要安裝教程的 **設定** 部分中提到的正確依賴項。
這應該會自動生成我們使用 gRPC 所需的原始碼。原始碼將放置在 -
Protobuf class code: python/greeting_pb2.py Protobuf gRPC code: python/greeting_pb2_grpcpb2.py
設定 gRPC 伺服器
現在我們已經定義了包含函式定義的 proto 檔案,讓我們設定一個可以呼叫這些函式的伺服器。
讓我們編寫伺服器程式碼來服務上述函式並將其儲存在 **server.py** 中 -
示例
from concurrent import futures
import grpc
import greeting_pb2
import greeting_pb2_grpc
class Greeter(greeting_pb2_grpc.GreeterServicer):
def greet(self, request, context):
print("Got request " + str(request))
return greeting_pb2.ServerOutput(message='{0} {1}!'.format(request.greeting, request.name))
def server():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=2))
greeting_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
server.add_insecure_port('[::]:50051')
print("gRPC starting")
server.start()
server.wait_for_termination()
server()
上面的程式碼在指定的埠啟動一個 gRPC 伺服器,併為我們在 proto 檔案中編寫的函式和服務提供服務。讓我們遍歷上面的程式碼 -
從 **main** 方法開始,我們在指定的埠建立一個 gRPC 伺服器。
但在啟動伺服器之前,我們將要執行的服務分配給伺服器,即在我們的例子中,是 **Greeter** 服務。
為此,我們需要將服務例項傳遞給伺服器,因此我們繼續建立服務例項,即在我們的例子中,是 **Greeter**。
服務例項需要提供 **.proto** 檔案中存在的 method/function 的實現,即在我們的例子中,是 **greet** 方法。
該方法期望一個與 .proto 檔案中定義的型別相同的物件,即對我們來說,是 **request**。
該方法處理上述輸入,進行計算,然後應該在 **.proto** 檔案中返回提到的輸出,即在我們的例子中,是 **ServerOutput**。
設定 gRPC 客戶端
現在我們已經編寫了伺服器的程式碼,讓我們設定一個可以呼叫這些函式的客戶端。
讓我們編寫客戶端程式碼來呼叫上述函式並將其儲存在 **client.py** 中 -
示例
import grpc
import greeting_pb2
import greeting_pb2_grpc
def run():
with grpc.insecure_channel('localhost:50051') as channel:
stub = greeting_pb2_grpc.GreeterStub(channel)
response = stub.greet(greeting_pb2.ClientInput(name='John', greeting = "Yo"))
print("Greeter client received following from server: " + response.message)
run()
上面的程式碼在指定的埠啟動一個 gRPC 伺服器,併為我們在 proto 檔案中編寫的函式和服務提供服務。讓我們遍歷上面的程式碼 -
從 **main** 方法開始,我們為與伺服器的 gRPC 通訊設定了一個 Channel。
然後,我們使用 channel 建立一個 **stub**。在這裡,我們使用計劃呼叫的函式的服務“**Greeter**”。stub 只不過是一個包裝器,它隱藏了遠端呼叫對呼叫者的複雜性。
然後,我們簡單地建立 proto 檔案中定義的預期輸入,即在我們的例子中,是 **ClientInput**。我們硬編碼了兩個引數,即 **name** 和 **greeting**。
我們最終進行呼叫並等待伺服器的結果。
所以,這就是我們的客戶端程式碼。
客戶端伺服器呼叫
現在,我們已經定義了我們的 **proto** 檔案,編寫了我們的伺服器和客戶端程式碼,讓我們繼續執行此程式碼並檢視實際情況。
要執行程式碼,請啟動兩個 shell。透過執行以下命令在第一個 shell 中啟動伺服器 -
python .\server.py
輸出
我們將得到以下輸出 -
gRPC starting
上述輸出表示伺服器已啟動。
現在,讓我們啟動客戶端。
python .\client.py
我們將看到以下輸出 -
輸出
Greeter client received following from server: Yo John!
現在,如果我們開啟伺服器日誌,我們將看到以下資料 -
gRPC starting Got request greeting: "Yo" name: "John"
因此,正如我們所看到的,客戶端能夠按預期呼叫伺服器,並且伺服器透過向客戶端回送問候來響應。