實戰篇:用 Python 搭建你的第一個 gRPC 服務(含版本坑的解決方案)

2025-12-18 11:39 | By justin | python gRPC
(Updated: 2025-12-20 05:18)

實戰篇:用 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 留言

目前沒有留言

發表留言
回覆