1. 概要
gRPC は、Google で開発された、言語やプラットフォームに依存しないリモート プロシージャ コール(RPC)フレームワークおよびツールセットです。これにより、プロトコル バッファ(特に強力なバイナリ シリアル化ツールセットおよび言語)を使用してサービスを定義できます。それにより、さまざまな言語で、サービス定義から慣用的なクライアント スタブとサーバースタブを生成できます。
この Codelab では、gRPC フレームワークを使用して API を公開する Java ベースのサービスを構築し、生成された gRPC クライアント側スタブを使用するクライアントを作成する方法を学びます。
学習内容
- プロトコル バッファ言語
- Java を使用して gRPC サービスを実装する方法
- Java を使用して gRPC クライアントを実装する方法
このチュートリアルをどのように使用しますか?
Node.js アプリの作成に関するご経験はどの程度ありますか?
Go アプリの作成に関するご経験についてお答えください。
<ph type="x-smartling-placeholder">2. 設定と要件
セルフペース型の環境設定
- Cloud コンソールにログインして、新しいプロジェクトを作成するか、既存のプロジェクトを再利用しますGmail アカウントも Google Workspace アカウントもまだお持ちでない場合は、アカウントを作成してください。
プロジェクト ID を忘れないようにしてください。プロジェクト ID はすべての Google Cloud プロジェクトを通じて一意の名前にする必要があります(上記の名前はすでに使用されているので使用できません)。以降、このコードラボでは PROJECT_ID
と呼びます。
- 次に、Google Cloud リソースを使用するために、Cloud Console で課金を有効にする必要があります。
このコードラボを実行しても、費用はほとんどかからないはずです。このチュートリアル以外で請求が発生しないように、リソースのシャットダウン方法を説明する「クリーンアップ」セクションの手順に従うようにしてください。Google Cloud の新規ユーザーは、$300 USD 分の無料トライアル プログラムをご利用いただけます。
Google Cloud Shell
この Codelab はパソコンからでも操作できますが、この Codelab では Google Cloud 上で動作するコマンドライン環境である Google Cloud Shell を使用します。
この Debian ベースの仮想マシンには、必要な開発ツールがすべて揃っています。永続的なホーム ディレクトリが 5 GB 用意されており、Google Cloud で稼働するため、ネットワークのパフォーマンスと認証が大幅に向上しています。つまり、この Codelab に必要なのはブラウザだけです(はい、Chromebook で動作します)。
- Cloud Console から Cloud Shell を有効にするには、[Cloud Shell をアクティブにする] をクリックします(環境のプロビジョニングと接続に若干時間を要します)。
Cloud Shell に接続すると、すでに認証は完了しており、プロジェクトに各自の PROJECT_ID
が設定されていることがわかります。
gcloud auth list
コマンド出力
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
コマンド出力
[core] project = <PROJECT_ID>
なんらかの理由でプロジェクトが設定されていない場合は、次のコマンドを実行します。
gcloud config set project <PROJECT_ID>
PROJECT_ID
が見つからない場合は、設定手順で使用した ID を確認するか、Cloud コンソール ダッシュボードで確認します。
Cloud Shell では、デフォルトで環境変数もいくつか設定されます。これらの変数は、以降のコマンドを実行する際に有用なものです。
echo $GOOGLE_CLOUD_PROJECT
コマンド出力
<PROJECT_ID>
- 最後に、デフォルトのゾーンとプロジェクト構成を設定します。
gcloud config set compute/zone us-central1-f
さまざまなゾーンを選択できます。詳しくは、リージョンとゾーン。
3. gRPC サービスを作成する
Maven で新しい Java プロジェクトを作成します。
$ mvn archetype:generate -DgroupId=com.example.grpc \ -DartifactId=grpc-hello-server \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DinteractiveMode=false $ cd grpc-hello-server
gRPC 定義ファイルを追加する
gRPC では、サービス ペイロード(リクエストとレスポンス)とサービス オペレーションを IDL(インターフェース定義言語)でキャプチャする必要があります。gRPC は Protobuffer 3 構文を使用してメッセージ ペイロードとオペレーションを定義します。Hello リクエストと Hello レスポンスがあるシンプルな Greeting Service の proto ファイルを作成してみましょう。
まず、新しい proto ファイルを格納する新しい proto ディレクトリを作成します。
$ mkdir -p src/main/proto
次に、新しい proto ファイル src/main/proto/GreetingService.proto
を作成します。
vim,nano,
または emacs
を使用してファイルを編集できます。
src/main/proto/GreetingService.proto
syntax = "proto3"; package com.example.grpc; // Request payload message HelloRequest { // Each message attribute is strongly typed. // You also must assign a "tag" number. // Each tag number is unique within the message. string name = 1; // This defines a strongly typed list of String repeated string hobbies = 2; // There are many more basics types, like Enum, Map // See https://developers.google.com/protocol-buffers/docs/proto3 // for more information. } message HelloResponse { string greeting = 1; } // Defining a Service, a Service can have multiple RPC operations service GreetingService { // Define a RPC operation rpc greeting(HelloRequest) returns (HelloResponse); }
gRPC の依存関係とプラグインを追加する
定義を作成したら、このファイルからサーバーサイド スタブとクライアントサイド スタブの両方を生成できます。gRPC の依存関係とプラグインを追加する必要があります。
まず、gRPC の依存関係を pom.xml
に追加します。
pom.xml
<project> ... <dependencies> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty-shaded</artifactId> <version>1.24.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>1.24.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>1.24.0</version> </dependency> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency> ... </dependencies> ... </project>
次に、プラグインを追加します。
pom.xml
<project> ... <dependencies> ... </dependencies> <build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.6.2</version> </extension> </extensions> <plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.6.1</version> <configuration> <protocArtifact>com.google.protobuf:protoc:3.9.0:exe:${os.detected.classifier}</protocArtifact> <pluginId>grpc-java</pluginId> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.24.0:exe:${os.detected.classifier}</pluginArtifact> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
スタブを生成する
アプリケーションのビルド時に、プラグインによって proto 定義が Java コードに変換されます。
$ mvn -DskipTests package
生成されたファイルを表示するには:
$ find target/generated-sources
Service を実装する
まず、greeting
オペレーションを実装する新しい GreetingServiceImpl
クラスを作成します。
src/main/java/com/example/grpc/GreetingServiceImpl.java
package com.example.grpc; import io.grpc.stub.StreamObserver; public class GreetingServiceImpl extends GreetingServiceGrpc.GreetingServiceImplBase { @Override public void greeting(GreetingServiceOuterClass.HelloRequest request, StreamObserver<GreetingServiceOuterClass.HelloResponse> responseObserver) { // HelloRequest has toString auto-generated. System.out.println(request); // You must use a builder to construct a new Protobuffer object GreetingServiceOuterClass.HelloResponse response = GreetingServiceOuterClass.HelloResponse.newBuilder() .setGreeting("Hello there, " + request.getName()) .build(); // Use responseObserver to send a single response back responseObserver.onNext(response); // When you are done, you must call onCompleted. responseObserver.onCompleted(); } }
サーバーの実装
最後に、ポートをリッスンするサーバーを起動し、このサービス実装を登録する必要があります。App
クラスとそのメインメソッドを編集します。
src/main/java/com/example/grpc/App.java
package com.example.grpc; import io.grpc.*; public class App { public static void main( String[] args ) throws Exception { // Create a new server to listen on port 8080 Server server = ServerBuilder.forPort(8080) .addService(new GreetingServiceImpl()) .build(); // Start the server server.start(); // Server threads are running in the background. System.out.println("Server started"); // Don't exit the main thread. Wait until server is terminated. server.awaitTermination(); } }
最後に、サーバーを実行します。
$ mvn -DskipTests package exec:java -Dexec.mainClass=com.example.grpc.App ... Server Started
4. Service の使用
ジェネレータは、クライアント側のスタブをすべて生成済みです。このラボではわかりやすくするために、同じ Maven プロジェクトを使用しますが、新しい main メソッドを持つ新しい Client
クラスを追加するだけです。
まず、[+] をクリックして新しい Cloud Shell セッションを開きます。サーバーを終了する必要はありません。
新しいセッションで、grpc-hello-server
ディレクトリに切り替えます。
$ cd grpc-hello-server
次に、新しい Client
クラスを追加します。
src/main/java/com/example/grpc/Client.java
package com.example.grpc; import io.grpc.*; public class Client { public static void main( String[] args ) throws Exception { // Channel is the abstraction to connect to a service endpoint // Let's use plaintext communication because we don't have certs final ManagedChannel channel = ManagedChannelBuilder.forTarget("localhost:8080") .usePlaintext(true) .build(); // It is up to the client to determine whether to block the call // Here we create a blocking stub, but an async stub, // or an async stub with Future are always possible. GreetingServiceGrpc.GreetingServiceBlockingStub stub = GreetingServiceGrpc.newBlockingStub(channel); GreetingServiceOuterClass.HelloRequest request = GreetingServiceOuterClass.HelloRequest.newBuilder() .setName("Ray") .build(); // Finally, make the call using the stub GreetingServiceOuterClass.HelloResponse response = stub.greeting(request); System.out.println(response); // A Channel should be shutdown before stopping the process. channel.shutdownNow(); } }
最後に、クライアントを実行します。
$ mvn -DskipTests package exec:java -Dexec.mainClass=com.example.grpc.Client ... greeting: "Hello there, Ray"
これで、簡単ですね
5. ストリーミング サービス
他にもいろいろ試すことができます。たとえば、proto ファイルの stream
キーワードをリクエストまたはレスポンス パラメータに追加するだけで、ストリーミング サービスを簡単に構築できます。次に例を示します。
src/main/proto/GreetingService.proto
syntax = "proto3"; package com.example.grpc; ... // Defining a Service, a Service can have multiple RPC operations service GreetingService { // MODIFY HERE: Update the return to streaming return. rpc greeting(HelloRequest) returns (stream HelloResponse); }
1 つではなく複数のレスポンスを送信するようにサーバーを更新します。そのためには、複数の responseObserver.onNext(...)
を呼び出します。
src/main/java/com/example/grpc/GreetingServiceImpl.java
package com.example.grpc; import io.grpc.stub.StreamObserver; public class GreetingServiceImpl extends GreetingServiceGrpc.GreetingServiceImplBase { @Override public void greeting(GreetingServiceOuterClass.HelloRequest request, StreamObserver<GreetingServiceOuterClass.HelloResponse> responseObserver) { ... // Feel free to construct different responses if you'd like. responseObserver.onNext(response); responseObserver.onNext(response); responseObserver.onNext(response); // When you are done, you must call onCompleted. responseObserver.onCompleted(); } }
クライアントは、ブロッキング スタブではなく非同期スタブを使用する必要があります。クライアント コードを更新し、stub
型を GreetingServiceStub
に更新します。
src/main/java/com/example/grpc/Client.java
package com.example.grpc; import io.grpc.*; // New import import io.grpc.stub.*; public class Client { public static void main( String[] args ) throws Exception { final ManagedChannel channel = ManagedChannelBuilder.forTarget("localhost:8080") .usePlaintext(true) .build(); // Replace the previous synchronous code with asynchronous code. // This time use an async stub: GreetingServiceGrpc.GreetingServiceStub stub = GreetingServiceGrpc.newStub(channel); // Construct a request GreetingServiceOuterClass.HelloRequest request = GreetingServiceOuterClass.HelloRequest.newBuilder() .setName("Ray") .build(); // Make an Asynchronous call. Listen to responses w/ StreamObserver stub.greeting(request, new StreamObserver<GreetingServiceOuterClass.HelloResponse>() { public void onNext(GreetingServiceOuterClass.HelloResponse response) { System.out.println(response); } public void onError(Throwable t) { } public void onCompleted() { // Typically you'll shutdown the channel somewhere else. // But for the purpose of the lab, we are only making a single // request. We'll shutdown as soon as this request is done. channel.shutdownNow(); } }); } }
アプリケーションを再ビルドします。
$ mvn -DskipTests package
個別の Cloud Shell セッションでサーバーとクライアントの両方を再起動します。
サーバーを起動するには:
$ mvn exec:java -Dexec.mainClass=com.example.grpc.App ... Server Started
クライアントを起動するには:
$ mvn exec:java -Dexec.mainClass=com.example.grpc.Client ... greeting: "Hello there, Ray" greeting: "Hello there, Ray" greeting: "Hello there, Ray"
6. 完了
学習した内容
- プロトコル バッファ言語
- Java を使用して gRPC サーバーを実装する方法
- Java を使用して gRPC クライアントを実装する方法
次のステップ:
- gRPC Java について学習する
- GitHub で gRPC Java の例を確認する
- gRPC でのストリーミングについて学習する
- gRPC から REST へのコード変換について学習する
フィードバックをお寄せください
- 簡単なアンケートにご協力ください