基本チュートリアル

PHPでのgRPCの基本的なチュートリアル入門です。

基本チュートリアル

PHPでのgRPCの基本的なチュートリアル入門です。

このチュートリアルでは、gRPCを操作するための基本的なPHPプログラマー向け入門を提供します。

この例を順に進めることで、次の方法を学びます。

  • .protoファイルでサービスを定義する。
  • プロトコルバッファコンパイラを使用してクライアントコードを生成する。
  • PHP gRPC APIを使用して、サービス用の簡単なクライアントを作成する。

プロトコルバッファに精通していることを前提としています。このチュートリアルの例では、プロトコルバッファ言語のproto2バージョンを使用していることに注意してください。

また、現在、PHPでgRPCサービスのクライアントを作成できるだけであることに注意してください。 gRPCサーバーを作成するには、別の言語を使用してください。

gRPCを使用する理由

私たちの例は、クライアントがルート上の機能に関する情報を取得し、ルートの概要を作成し、サーバーや他のクライアントと交通情報などのルート情報を交換できるシンプルなルートマッピングアプリケーションです。

gRPCを使用すると、.protoファイルでサービスを一度定義し、gRPCがサポートする任意の言語でクライアントとサーバーを生成できます。これにより、大規模なデータセンター内のサーバーから自分のタブレットまで、さまざまな環境で実行できます。異なる言語と環境間の通信の複雑さはすべてgRPCによって処理されます。効率的なシリアル化、シンプルなIDL、簡単なインターフェースの更新など、プロトコルバッファを使用するすべての利点も得られます。

サンプルコードとセットアップ

チュートリアルのサンプルコードは、grpc/grpc/examples/php/route_guideにあります。例をダウンロードするには、次のコマンドを実行してgrpcリポジトリとそのサブモジュールを複製します。

$ git clone --recurse-submodules -b v1.62.0 --depth 1 --shallow-submodules https://github.com/grpc/grpc

.protoファイルのコンパイルを支援するには、grpc-php-pluginが必要です。次のようにソースからビルドします。

$ cd grpc
$ mkdir -p cmake/build
$ pushd cmake/build
$ cmake ../..
$ make protoc grpc_php_plugin
$ popd

次に、ルートガイドディレクトリに変更し、例の.protoファイルをコンパイルします。

$ cd examples/php/route_guide
$ ./route_guide_proto_gen.sh

私たちの例は、クライアントがルート上の機能に関する情報を取得し、ルートの概要を作成し、サーバーや他のクライアントと交通情報などのルート情報を交換できるシンプルなルートマッピングアプリケーションです。

また、(テスト用の別の言語で)クライアントインターフェースコードを生成するための関連ツールがインストールされている必要があります。たとえば、これらのセットアップ手順に従って後者を入手できます。

試してみよう!

サンプルアプリを試すには、ローカルでgRPCサーバーを実行する必要があります。たとえば、このリポジトリでNode.jsサーバーをコンパイルして実行してみましょう。

$ cd ../../node
$ npm install
$ cd dynamic_codegen/route_guide
$ nodejs ./route_guide_server.js --db_path=route_guide_db.json

PHPクライアントを(別のターミナルで)実行します。

$ ./run_route_guide_client.sh

次のセクションでは、このprotoサービスがどのように定義されているか、それからクライアントライブラリを生成する方法、およびそのライブラリを使用するクライアントスタブを作成する方法について、ステップバイステップで説明します。

サービス定義

まず、使用しているサービスの定義方法を見てみましょう。 プロトコルバッファを使用して、gRPC *サービス*とそのメソッドの*リクエスト*と*レスポンス*の型を定義します。例の完全な.protoファイルは、examples/protos/route_guide.protoで確認できます。

サービスを定義するには、.protoファイルで名前付きのserviceを指定します。

service RouteGuide {
   ...
}

次に、サービス定義内でrpcメソッドを定義し、それらのリクエストとレスポンスの型を指定します。プロトコルバッファを使用すると、4種類のサービスメソッドを定義できます。これらはすべてRouteGuideサービスで使用されます。

  • 単純なRPC。クライアントがサーバーにリクエストを送信し、後でレスポンスを受信する、通常の遠隔手続き呼び出しと同じです。

    // Obtains the feature at a given position.
    rpc GetFeature(Point) returns (Feature) {}
    
  • レスポンスストリーミングRPC。クライアントがサーバーにリクエストを送信し、レスポンスメッセージのストリームを取得します。レスポンスストリーミングメソッドを指定するには、レスポンス型の前にstreamキーワードを配置します。

    // Obtains the Features available within the given Rectangle.  Results are
    // streamed rather than returned at once (e.g. in a response message with a
    // repeated field), as the rectangle may cover a large area and contain a
    // huge number of features.
    rpc ListFeatures(Rectangle) returns (stream Feature) {}
    
  • リクエストストリーミングRPC。クライアントがサーバーにメッセージのシーケンスを送信します。クライアントがメッセージの書き込みを完了すると、サーバーがそれらをすべて読み取り、レスポンスを返すのを待ちます。リクエストストリーミングメソッドを指定するには、リクエスト型の前にstreamキーワードを配置します。

    // Accepts a stream of Points on a route being traversed, returning a
    // RouteSummary when traversal is completed.
    rpc RecordRoute(stream Point) returns (RouteSummary) {}
    
  • 双方向ストリーミングRPC。両側がメッセージのシーケンスを互いに送信します。2つのストリームは独立して動作するため、クライアントとサーバーは好きな順序で読み書きできます。たとえば、サーバーはすべてのクライアントメッセージを受信してからレスポンスを書き込むのを待つことも、メッセージを読み取ってからメッセージを書き込むことも、読み取りと書き込みの他の組み合わせを行うこともできます。各ストリームのメッセージの順序は保持されます。このタイプのメソッドを指定するには、リクエストとレスポンスの両方の前にstreamキーワードを配置します。

    // Accepts a stream of RouteNotes sent while a route is being traversed,
    // while receiving other RouteNotes (e.g. from other users).
    rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
    

また、.protoファイルには、サービスメソッドで使用されるすべてのリクエストとレスポンスのタイプのプロトコルバッファメッセージタイプ定義も含まれています。たとえば、Pointメッセージタイプを以下に示します。

// Points are represented as latitude-longitude pairs in the E7 representation
// (degrees multiplied by 10**7 and rounded to the nearest integer).
// Latitudes should be in the range +/- 90 degrees and longitude should be in
// the range +/- 180 degrees (inclusive).
message Point {
  int32 latitude = 1;
  int32 longitude = 2;
}

クライアントコードの生成

protoファイルのPHPクライアントスタブ実装は、gRPC PHP Protoc Pluginによって生成できます。プラグインをコンパイルするには

$ make grpc_php_plugin

クライアントスタブ実装の.phpファイルを生成するには

$ cd grpc
$ protoc --proto_path=examples/protos \
  --php_out=examples/php/route_guide \
  --grpc_out=examples/php/route_guide \
  --plugin=protoc-gen-grpc=bins/opt/grpc_php_plugin \
  ./examples/protos/route_guide.proto

または、grpc-php-pluginをソースからビルドする場合は、grpc/example/php/route_guideディレクトリの下にあるヘルパースクリプトを実行します。

$ ./route_guide_proto_gen.sh

examples/php/route_guideディレクトリに多数のファイルが生成されます。これらのファイルを変更する必要はありません。

これらの生成されたファイルをロードするには、examples/phpディレクトリの下にあるcomposer.jsonファイルにこのセクションを追加します。

  "autoload": {
    "psr-4": {
      "": "route_guide/"
    }
  }

このファイルには次のものが含まれています。

  • リクエストメッセージタイプとレスポンスメッセージタイプを設定、シリアル化、および取得するためのすべてのプロトコルバッファコード。
  • クライアントがRouteGuideサービスで定義されているメソッドを呼び出すことができるRouteguide\RouteGuideClientという名前のクラス。

クライアントの作成

このセクションでは、RouteGuideサービス用のPHPクライアントの作成を見ていきます。完全なクライアントコード例は、examples/php/route_guide/route_guide_client.phpで確認できます。

クライアントオブジェクトの構築

サービスメソッドを呼び出すには、まず、生成されたRouteGuideClientクラスのインスタンスであるクライアントオブジェクトを作成する必要があります。クラスのコンストラクターは、接続先のサーバーアドレスとポートを必要とします。

$client = new Routeguide\RouteGuideClient('localhost:50051', [
    'credentials' => Grpc\ChannelCredentials::createInsecure(),
]);

サービスメソッドの呼び出し

次に、サービスメソッドの呼び出し方を見てみましょう。

単純なRPC

単純なRPC GetFeatureの呼び出しは、ローカルの非同期メソッドの呼び出しとほぼ同じくらい簡単です。

$point = new Routeguide\Point();
$point->setLatitude(409146138);
$point->setLongitude(-746188906);
list($feature, $status) = $client->GetFeature($point)->wait();

ご覧のとおり、リクエストオブジェクト、つまりRouteguide\Pointオブジェクトを作成して設定します。次に、リクエストオブジェクトを渡して、スタブでメソッドを呼び出します。エラーがない場合は、レスポンスオブジェクト、つまりRouteguide\Featureオブジェクトからサーバーからのレスポンス情報を読み取ることができます。

print sprintf("Found %s \n  at %f, %f\n", $feature->getName(),
              $feature->getLocation()->getLatitude() / COORD_FACTOR,
              $feature->getLocation()->getLongitude() / COORD_FACTOR);
ストリーミングRPC

次に、ストリーミングメソッドを見てみましょう。ここでは、地理的なFeatureのストリームを返すサーバー側のストリーミングメソッドListFeaturesを呼び出します。

$lo_point = new Routeguide\Point();
$hi_point = new Routeguide\Point();

$lo_point->setLatitude(400000000);
$lo_point->setLongitude(-750000000);
$hi_point->setLatitude(420000000);
$hi_point->setLongitude(-730000000);

$rectangle = new Routeguide\Rectangle();
$rectangle->setLo($lo_point);
$rectangle->setHi($hi_point);

$call = $client->ListFeatures($rectangle);
// an iterator over the server streaming responses
$features = $call->responses();
foreach ($features as $feature) {
  // process each feature
} // the loop will end when the server indicates there is no more responses to be sent.

$call->responses()メソッド呼び出しは、イテレーターを返します。サーバーがレスポンスを送信すると、foreachループで$featureオブジェクトが返されます。これは、サーバーが送信するレスポンスがなくなることを示すまで続きます。

クライアント側のストリーミングメソッドRecordRouteも同様です。クライアント側から書き込む各ポイントに対して$call->write($point)を呼び出し、Routeguide\RouteSummaryを取得する点が異なります。

$call = $client->RecordRoute();

for ($i = 0; $i < $num_points; $i++) {
  $point = new Routeguide\Point();
  $point->setLatitude($lat);
  $point->setLongitude($long);
  $call->write($point);
}

list($route_summary, $status) = $call->wait();

最後に、双方向ストリーミングRPC routeChat()を見てみましょう。この場合、メソッドにコンテキストを渡すだけで、メッセージの書き込みと読み取りの両方に使用できるBidiStreamingCallストリームオブジェクトが返されます。

$call = $client->RouteChat();

クライアントからメッセージを書き込むには

foreach ($notes as $n) {
  $point = new Routeguide\Point();
  $point->setLatitude($lat = $n[0]);
  $point->setLongitude($long = $n[1]);

  $route_note = new Routeguide\RouteNote();
  $route_note->setLocation($point);
  $route_note->setMessage($message = $n[2]);
  $call->write($route_note);
}
$call->writesDone();

サーバーからメッセージを読み取るには

while ($route_note_reply = $call->read()) {
  // process $route_note_reply
}

各サイドは常に書き込まれた順序で相手のメッセージを取得しますが、クライアントとサーバーは任意の順序で読み書きできます。ストリームは完全に独立して動作します。