gRPC-Python スタートガイド

1. はじめに

この Codelab では、gRPC-Python を使用して、Python で記述されたルート マッピング アプリケーションの基盤となるクライアントとサーバーを作成します。

このチュートリアルを完了すると、gRPC を使用してリモート サーバーに接続し、地図上の特定の座標にあるものの名前または郵便番号を取得するクライアントが作成されます。本格的なアプリケーションでは、このクライアント サーバー設計を使用して、ルート上のスポットを列挙または要約できます。

サービスは Protocol Buffers ファイルで定義されます。このファイルは、クライアントとサーバーが相互に通信できるように、クライアントとサーバーのボイラープレート コードを生成するために使用されます。これにより、この機能を実装する時間と労力を節約できます。

この生成されたコードは、サーバーとクライアント間の通信の複雑さだけでなく、データのシリアル化と逆シリアル化も処理します。

学習内容

  • プロトコル バッファを使用してサービス API を定義する方法。
  • 自動コード生成を使用して、プロトコル バッファ定義から gRPC ベースのクライアントとサーバーを構築する方法。
  • gRPC を使用したクライアント サーバー通信の理解。

この Codelab は、gRPC を初めて使用する Python デベロッパー、gRPC の復習を希望するデベロッパー、分散システムの構築に関心のある方を対象としています。gRPC の経験は必要ありません。

2. 始める前に

必要なもの

  • Python 3.9 以降。Python 3.13 をおすすめします。プラットフォーム固有のインストール手順については、Python の設定と使用をご覧ください。または、uv pyenv などのツールを使用して、システム以外の Python をインストールします。
  • Python パッケージをインストールする pip
  • Python 仮想環境を作成する venv

ensurepip パッケージと venv パッケージは Python 標準ライブラリの一部であり、通常はデフォルトで使用できます。

ただし、Debian ベースのディストリビューション(Ubuntu など)では、Python を再配布する際にこれらを除外することがあります。パッケージをインストールするには、次のコマンドを実行します。

sudo apt install python3-pip python3-venv

コードを取得する

学習を効率化するため、この Codelab では、すぐに始められるように、あらかじめ作成されたソースコード スキャフォールドが用意されています。次の手順では、grpc_tools.protoc Protocol Buffer コンパイラ プラグインを使用した gRPC コード生成など、アプリケーションの完成までの手順について説明します。

grpc-codelabs

この Codelab のスキャフォールディング ソースコードは、codelabs/grpc-python-getting-started/start_here ディレクトリにあります。コードを自分で実装しない場合は、完成したソースコードが completed ディレクトリにあります。

まず、Codelab の作業ディレクトリを作成し、そのディレクトリに移動します。

mkdir grpc-python-getting-started && cd grpc-python-getting-started

Codelab をダウンロードして解凍します。

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 メソッド、リクエストとレスポンスのメッセージ タイプを定義します。サービスでは以下を提供します。

  • サーバーが実装し、クライアントが呼び出す GetFeature という RPC メソッド。
  • メッセージ タイプ PointFeature は、GetFeature メソッドを使用するときにクライアントとサーバー間で交換されるデータ構造です。クライアントは、サーバーへの GetFeature リクエストで地図の座標を Point として提供し、サーバーはそれらの座標にあるものを説明する対応する Feature で応答します。

この RPC メソッドとそのメッセージ型はすべて、提供されたソースコードの protos/route_guide.proto ファイルで定義されます。

Protocol Buffers は一般に protobuf と呼ばれます。gRPC の用語の詳細については、gRPC の コア コンセプト、アーキテクチャ、ライフサイクルをご覧ください。

メッセージのタイプ

ソースコードの protos/route_guide.proto ファイルで、まず Point メッセージ タイプを定義します。Point は、地図上の緯度と経度の座標ペアを表します。この Codelab では、座標に整数を使用します。

message Point {
  int32 latitude = 1;
  int32 longitude = 2;
}

数値 12 は、message 構造内の各フィールドの一意の ID 番号です。

次に、Feature メッセージ タイプを定義します。Feature は、Point で指定された場所にあるものの名前または郵便番号に string フィールドを使用します。

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 ファイルには、アプリケーションのサービスによって提供される 1 つ以上のメソッドを定義する RouteGuide という名前の service 構造があります。

RouteGuide 定義内に rpc メソッド GetFeature を追加します。前述のとおり、このメソッドは指定された座標セットから場所の名前または住所を検索するため、指定された Point に対して GetFeatureFeature を返すようにします。

service RouteGuide {
  // Definition of the service goes here

  // Obtains the feature at a given position.
  rpc GetFeature(Point) returns (Feature) {}
}

これは単項 RPC メソッドです。クライアントがサーバーにリクエストを送信し、ローカル関数呼び出しと同様にレスポンスが返ってくるのを待つシンプルな RPC です。

4. クライアント コードとサーバーコードを生成する

次に、プロトコル バッファ コンパイラを使用して、.proto ファイルからクライアントとサーバーの両方のボイラープレート gRPC コードを生成します。

gRPC Python コード生成用に、grpcio-tools を作成しました。次の内容が含まれます。

  1. message 定義から Python コードを生成する通常の protoc コンパイラ。
  2. service 定義から Python コード(クライアントとサーバースタブ)を生成する gRPC protobuf プラグイン。

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 で定義したインターフェースに対して次のファイルが生成されます。

  1. route_guide_pb2.py には、message 定義から生成されたクラスを動的に作成するコードが含まれています。
  2. route_guide_pb2.pyi は、message 定義から生成された「スタブファイル」または「型ヒントファイル」です。実装のないシグネチャのみが含まれています。スタブファイルは、IDE でより優れたオートコンプリートとエラー検出を提供するために使用できます。
  3. route_guide_pb2_grpc.pyservice 定義から生成され、gRPC 固有のクラスと関数が含まれています。

gRPC 固有のコードには次のものが含まれます。

  1. RouteGuideStub。これは、gRPC クライアントが RouteGuide RPC を呼び出すために使用できます。
  2. RouteGuideServicer。これは、RouteGuide サービスの実装のインターフェースを定義します。
  3. RouteGuideServicergRPC サーバーに登録するために使用される add_RouteGuideServicer_to_server 関数。

5. サービスを作成する

まず、RouteGuide サーバーの作成方法を見てみましょう。RouteGuide サーバーの作成と実行は、次の 2 つの作業項目に分かれます。

  • サービス定義から生成されたサービサー インターフェースを、サービスの実際の「作業」を行う関数で実装します。
  • 特定のポートで gRPC サーバーを実行して、クライアントからのリクエストをリッスンし、レスポンスを送信する。

初期の RouteGuide サーバーは start_here/route_guide_server.py にあります。

RouteGuide を実装する

route_guide_server.py には、生成されたクラス route_guide_pb2_grpc.RouteGuideServicer をサブクラス化する 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 で確認できます。

スタブを作成する

サービス メソッドを呼び出すには、まずスタブを作成する必要があります。

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 は同期(ブロッキング)と非同期(ノンブロッキング)の両方の制御フロー セマンティクスをサポートします。

Simple 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. 次のステップ