1. 簡介
在本程式碼研究室中,您將使用 gRPC-Python 建立用戶端和伺服器,做為以 Python 編寫的路線對應應用程式基礎。
完成本教學課程後,您將擁有一個用戶端,可使用 gRPC 連線至遠端伺服器,取得地圖上特定座標位置的名稱或郵寄地址。完整的應用程式可能會使用這種用戶端/伺服器設計,列舉或摘要說明路線上的興趣點。
服務定義於 Protocol Buffers 檔案中,用於產生用戶端和伺服器的樣板程式碼,以便彼此通訊,節省您實作該功能的時間和精力。
這段產生的程式碼不僅會處理伺服器與用戶端之間複雜的通訊,也會處理資料序列化和還原序列化。
課程內容
- 如何使用通訊協定緩衝區定義服務 API。
- 如何使用自動程式碼生成功能,從通訊協定緩衝區定義建構以 gRPC 為基礎的用戶端和伺服器。
- 瞭解如何透過 gRPC 進行用戶端與伺服器之間的通訊。
這個程式碼研究室適用於剛接觸 gRPC 或想複習 gRPC 的 Python 開發人員,以及有興趣建構分散式系統的任何人。不需要有 gRPC 相關經驗。
2. 事前準備
軟硬體需求
- Python 3.9 以上版本。建議使用 Python 3.13。如需特定平台的安裝說明,請參閱「Python 設定和使用方式」。或者,您也可以使用 uv 或 pyenv 等工具,安裝非系統 Python。
- pip 安裝 Python 套件。
- venv 建立 Python 虛擬環境。
ensurepip
和 venv
套件是 Python 標準程式庫的一部分,通常預設會提供。
不過,部分以 Debian 為基礎的發行版本 (包括 Ubuntu) 會選擇在重新發布 Python 時排除這些檔案。如要安裝套件,請執行:
sudo apt install python3-pip python3-venv
取得程式碼
為簡化學習過程,本程式碼研究室提供預先建構的原始碼架構,協助您踏出第一步。下列步驟將引導您完成應用程式,包括使用 grpc_tools.protoc
Protocol Buffer 編譯器外掛程式產生 gRPC 程式碼。
grpc-codelabs
本程式碼研究室的架構原始碼位於 codelabs/grpc-python-getting-started/start_here 目錄。如果您不想自行導入程式碼,完成的原始碼會放在 completed
目錄中。
首先,請建立程式碼研究室工作目錄,然後 cd 到該目錄:
mkdir grpc-python-getting-started && cd grpc-python-getting-started
下載並擷取程式碼研究室:
curl -sL https://github.com/grpc-ecosystem/grpc-codelabs/archive/refs/heads/v1.tar.gz \
| tar xvz --strip-components=4 \
grpc-codelabs-1/codelabs/grpc-python-getting-started/start_here
或者,您也可以下載只包含 Codelab 目錄的 .zip 檔案,然後手動解壓縮。
3. 定義服務
首先,請使用通訊協定緩衝區介面定義語言,定義應用程式的 gRPC 服務、RPC 方法,以及要求和回應訊息類型。你的服務將提供:
- 伺服器實作且用戶端呼叫的 RPC 方法,稱為
GetFeature
。 - 使用
GetFeature
方法時,訊息類型Point
和Feature
是用戶端與伺服器之間交換的資料結構。用戶端會在傳送至伺服器的GetFeature
要求中,以Point
形式提供地圖座標,而伺服器會回覆相應的Feature
,說明這些座標所指的位置。
這個 RPC 方法及其訊息型別都會在提供的原始碼 protos/route_guide.proto
檔案中定義。
通訊協定緩衝區通常稱為 protobuf。如要進一步瞭解 gRPC 術語,請參閱 gRPC 的「核心概念、架構和生命週期」。
訊息類型
在原始碼的 protos/route_guide.proto
檔案中,請先定義 Point
訊息型別。Point
代表地圖上的經緯度座標組合。在本程式碼研究室中,請使用整數做為座標:
message Point {
int32 latitude = 1;
int32 longitude = 2;
}
數字 1
和 2
是 message
結構中每個欄位的專屬 ID 編號。
接著,定義 Feature
訊息類型。Feature
會使用 string
欄位,指定 Point
所指定位置的名稱或郵寄地址:
message Feature {
// The name or address of the feature.
string name = 1;
// The point where the feature is located.
Point location = 2;
}
服務方法
route_guide.proto
檔案具有名為 RouteGuide
的 service
結構,可定義應用程式服務提供的一或多個方法。
在 RouteGuide
定義中新增 rpc
方法 GetFeature
。如先前所述,這個方法會根據一組指定座標查閱地點名稱或地址,因此請讓 GetFeature
針對指定 Point
傳回 Feature
:
service RouteGuide {
// Definition of the service goes here
// Obtains the feature at a given position.
rpc GetFeature(Point) returns (Feature) {}
}
這是 unary RPC 方法:簡單的 RPC,用戶端會將要求傳送至伺服器,並等待伺服器傳回回應,就像呼叫本機函式一樣。
4. 產生用戶端和伺服器程式碼
接著,使用通訊協定緩衝區編譯器,從 .proto
檔案產生用戶端和伺服器的樣板 gRPC 程式碼。
我們建立了 grpcio-tools,用於生成 gRPC Python 程式碼。It includes:
- 可從
message
定義產生 Python 程式碼的標準 protoc 編譯器。 - gRPC protobuf 外掛程式,可從
service
定義產生 Python 程式碼 (用戶端和伺服器存根)。
我們將使用 pip 安裝 grpcio-tools
Python 套件。讓我們建立新的 Python 虛擬環境 (venv),將專案的依附元件與系統套件區隔開來:
python3 -m venv --upgrade-deps .venv
如要在 bash/zsh 殼層中啟動虛擬環境,請執行下列指令:
source .venv/bin/activate
如為 Windows 和非標準殼層,請參閱 https://docs.python.org/3/library/venv.html#how-venvs-work 中的表格。
接著,請安裝 grpcio-tools (這也會安裝 grpcio 套件):
pip install grpcio-tools
使用下列指令產生 Python 樣板程式碼:
python -m grpc_tools.protoc --proto_path=./protos \
--python_out=. --pyi_out=. --grpc_python_out=. \
./protos/route_guide.proto
這會為我們在 route_guide.proto
中定義的介面產生下列檔案:
route_guide_pb2.py
包含動態建立類別的程式碼,這些類別是從message
定義產生。route_guide_pb2.pyi
是從message
定義產生的「存根檔案」或「型別提示檔案」。其中只包含沒有實作的簽章。IDE 可使用 Stub 檔案,提供更完善的自動完成和錯誤偵測功能。route_guide_pb2_grpc.py
是從service
定義產生,包含 gRPC 專屬的類別和函式。
gRPC 專屬程式碼包含:
5. 建立服務
首先,我們來看看如何建立 RouteGuide
伺服器。建立及執行 RouteGuide
伺服器可分為兩項工作:
- 實作從服務定義產生的服務介面,並使用可執行服務實際「工作」的函式。
- 在特定通訊埠執行 gRPC 伺服器,監聽來自用戶端的要求並傳輸回應。
您可以在 start_here/route_guide_server.py
中找到初始 RouteGuide
伺服器。
實作 RouteGuide
route_guide_server.py
具有 RouteGuideServicer
類別,可將產生的類別 route_guide_pb2_grpc.RouteGuideServicer
子類別化:
# RouteGuideServicer provides an implementation
# of the methods of the RouteGuide service.
class RouteGuideServicer(route_guide_pb2_grpc.RouteGuideServicer):
RouteGuideServicer
會實作所有 RouteGuide
服務方法。
現在來詳細瞭解簡單的 RPC 實作方式。方法 GetFeature
會從用戶端取得 Point
,並從資料庫中傳回 Feature
的對應功能資訊。
def GetFeature(self, request, context):
feature = get_feature(self.db, request)
if feature is None:
return route_guide_pb2.Feature(name="", location=request)
else:
return feature
這個方法會傳遞 RPC 的 route_guide_pb2.Point
請求,以及提供 RPC 專屬資訊 (例如逾時限制) 的 grpc.ServicerContext
物件。系統會傳回 route_guide_pb2.Feature
回應。
啟動伺服器
實作所有 RouteGuide
方法後,下一步是啟動 gRPC 伺服器,讓用戶端實際使用您的服務:
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
route_guide_pb2_grpc.add_RouteGuideServicer_to_server(
RouteGuideServicer(),
server,
)
listen_addr = "localhost:50051"
server.add_insecure_port(listen_addr)
print(f"Starting server on {listen_addr}")
server.start()
server.wait_for_termination()
伺服器 start()
方法不會封鎖。系統會例項化新執行緒來處理要求。呼叫 server.start()
的執行緒通常不會有其他工作要做。在這種情況下,您可以呼叫 server.wait_for_termination()
,乾淨地封鎖呼叫執行緒,直到伺服器終止為止。
6. 建立用戶端
在本節中,我們將說明如何為 RouteGuide
服務建立用戶端。您可以在 start_here/route_guide_client.py
中查看初始用戶端程式碼。
建立存根
如要呼叫服務方法,我們必須先建立 stub。
我們會在 route_guide_client.py
檔案中,從 .proto
內例項化 route_guide_pb2_grpc
模組的 RouteGuideStub
類別。
channel = grpc.insecure_channel("localhost:50051")
stub = route_guide_pb2_grpc.RouteGuideStub(channel)
呼叫服務方法
對於傳回單一回應的 RPC 方法 (稱為「回應一元」方法),gRPC Python 同時支援同步 (封鎖) 和非同步 (非封鎖) 控制流程語意。
簡單 RPC
首先,請定義要用來呼叫服務的 Point
。這應該很簡單,只要從 route_guide_pb2
套件例項化物件,並提供一些屬性即可:
point = route_guide_pb2.Point(latitude=412346009, longitude=-744026814)
同步呼叫簡單的 RPC GetFeature
,幾乎與呼叫本機方法一樣簡單。RPC 呼叫會等待伺服器回應,並傳回回應或引發例外狀況。我們可以呼叫方法並查看回應,如下所示:
feature = stub.GetFeature(point)
print(feature)
您可以檢查 Feature 物件的欄位,並輸出要求的結果:
if feature.name:
print(f"Feature called '{feature.name}' at {format_point(feature.location)}")
else:
print(f"Found no feature at at {format_point(feature.location)}")
7. 立即試用
執行伺服器:
python route_guide_server.py
在其他終端機中再次啟動虛擬環境,然後執行用戶端:
python route_guide_client.py
您會看到類似以下的輸出內容,為求清楚起見,時間戳記已省略:
name: "16 Old Brook Lane, Warwick, NY 10990, USA" location { latitude: 412346009 longitude: -744026814 } Feature called '16 Old Brook Lane, Warwick, NY 10990, USA' at latitude: 412346009, longitude: -744026814
8. 後續步驟
- 請參閱「gRPC 簡介」和「核心概念」,瞭解 gRPC 的運作方式
- 逐步完成基礎教學課程
- 探索 Python API 參考資料