實戰篇:用 Python 搭建你的第一個 gRPC 服務(含版本坑的解決方案) 各位好! 上一篇我們聊完了 gRPC 的理論,知道它很快、很強。但光說不練,今天我們就捲起袖子,用 Python 來實作一個最經典的「Hello World」遠端調用。 這次的目標很簡單: 寫一個 .proto 定義檔。 生成 Python 程式碼(並解決那個煩人的版本檢測問題)。 寫一個 Server,寫一個 Client,讓它們成功對話。
第一步:環境準備 首先,為了不讓你的 Python 環境變髒,建議先開一個虛擬環境(也可以不使用)。接著我們需要安裝兩個套件:
grpcio:gRPC 的核心庫。 grpcio-tools:包含 protoc 編譯器和 Python 插件,用來生成程式碼。
pip install grpcio grpcio-tools
小提醒:請記住你現在安裝的版本。這跟等一下要講的「坑」有關。
第二步:定義合約 (.proto) 我們先建立一個專案資料夾,裡面新增一個 helloworld.proto。這就是我們雙方的合約:
syntax = "proto3";
package helloworld;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
第三步:生成程式碼(與解決版本檢測坑) 這是最關鍵的一步。我們要用工具把上面的 .proto 變成 Python 看得懂的 .py 檔。
在終端機執行:
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. helloworld.proto
執行完後,你會發現目錄下多了兩個檔案: helloworld_pb2.py:定義了 Request/Reply 的資料結構。 helloworld_pb2_grpc.py:定義了 Client Stub 和 Server Servicer 的類別。
接下來是上一篇提到的「版本檢測坑」 打開 helloworld_pb2.py,你可能會在開頭看到類似這樣的程式碼:
# ... 前面有一堆 import
from google.protobuf import runtime_version
# 這裡就是兇手!
if runtime_version.DOMAIN_NAME == "google.protobuf":
if runtime_version.MAJOR < 5:
raise RuntimeError("Protobuf runtime version mismatch...")
為什麼會有這個? 這是為了防止你「生成程式的工具版本」和「實際執行環境的庫版本」不一致導致的潛在錯誤。 什麼時候會報錯? 比如你在開發機上用最新的 grpcio-tools (v1.60) 生成了代碼,結果丟到一台舊的 Server 上跑,那邊只有 grpcio (v1.48)。程式一跑起來就會直接 Crash,告訴你版本不對。 怎麼解決?
正規解法(推薦):確保開發環境、CI/CD 流程和生產環境的 grpcio 版本完全鎖定一致。
暴力解法(救急用):如果你無法控制 Server 的環境版本,或者只是想先讓它跑起來,你可以直接註解掉或刪除這段檢查代碼。
第四步:實作 Server 搞定程式生成後,我們來寫伺服器端 server.py。這裡我們要繼承剛剛生成的 GreeterServicer。
import logging
from concurrent import futures
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
class Greeter(helloworld_pb2_grpc.GreeterServicer):
# 實作我們在 proto 裡定義的 SayHello 方法
def SayHello(self, request, context):
print(f"收到來自 {request.name} 的請求!")
return helloworld_pb2.HelloReply(message=f'Hello, {request.name}!')
def serve():
# gRPC 伺服器通常使用 ThreadPool 來處理併發請求
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
# 將我們實作的 Greeter 註冊到 Server 中
helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
# 監聽 Port 50051
server.add_insecure_port('[::]:50051')
print("Server 啟動中,監聽 port 50051...")
server.start()
# 讓 Server 持續運行
server.wait_for_termination()
if __name__ == '__main__':
logging.basicConfig()
serve()
第五步:實作 Client 最後是客戶端 client.py
import logging
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
def run():
# 建立連線通道 (Channel)
# 注意:在生產環境通常會需要 SSL/TLS (secure_channel),這裡測試用 insecure
# 注意!如果你運行在不同台電腦,你會需要更改這裡的地址及端口
with grpc.insecure_channel('localhost:50051') as channel:
# 建立 Stub (客戶端代理人)
stub = helloworld_pb2_grpc.GreeterStub(channel)
# 呼叫遠端方法,就像呼叫本地函數一樣!
response = stub.SayHello(helloworld_pb2.HelloRequest(name='Python大神'))
print(f"Server 回傳: {response.message}")
if __name__ == '__main__':
logging.basicConfig()
run()
跑起來! 先打開一個終端跑 Server:
python server.py
#輸出: Server 啟動中,監聽 port 50051...
打開另一個終端跑 Client:
python client.py
輸出: Server 回傳: Hello, Python大神!
與此同時,你的 Server 端應該會顯示:收到來自 Python大神 的請求! 小結 恭喜!你已經完成了一個最基礎的 gRPC 應用。 你會發現,寫 gRPC 的過程其實就是:定義介面 -> 生成程式-> 填空題(實作邏輯)。 雖然我們用 Python 實作了兩端,但 gRPC 的魅力在於,你可以保留這個 server.py,然後轉頭去用 Go 或 Java 再寫一個 Client,只要 .proto 檔一樣,它們就能完美溝通。 下一次,如果大家有興趣,我們可以來聊聊更進階的場景:如何在 gRPC 中傳輸大檔案(Streaming 實戰),或是如何加入攔截器(Interceptor)來做 Log 紀錄和權限驗證。
0 留言
發表留言