1. 序章

gRPCは、最初にGoogleによって開発された高性能のオープンソースRPCフレームワークです。ボイラープレートコードを排除し、データセンター内およびデータセンター間でポリグロットサービスを接続するのに役立ちます。

2. 概要

フレームワークは、リモートプロシージャコールのクライアントサーバーモデルに基づいています。 クライアントアプリケーションは、ローカルオブジェクトであるかのように、サーバーアプリケーションのメソッドを直接呼び出すことができます。

この記事では、次の手順を使用して、gRPCを使用して一般的なクライアントサーバーアプリケーションを作成します。

  1. .protoファイルでサービスを定義します
  2. プロトコルバッファコンパイラを使用してサーバーとクライアントのコードを生成する
  3. サーバーアプリケーションを作成し、生成されたサービスインターフェイスを実装し、gRPCサーバーを生成します
  4. 生成されたスタブを使用してRPC呼び出しを行い、クライアントアプリケーションを作成します

姓と名の代わりに挨拶を返す単純なHelloServiceを定義しましょう。

3. Mavenの依存関係

grpc- netty grpc-protobuf grpc-stubの依存関係を追加しましょう。

<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-netty</artifactId>
    <version>1.16.1</version>
</dependency>
<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-protobuf</artifactId>
    <version>1.16.1</version>
</dependency>
<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-stub</artifactId>
    <version>1.16.1</version>
</dependency>

4. サービスの定義

まず、サービスを定義し、パラメータとリターンタイプとともにリモートで呼び出すことができるメソッドを指定します。

これは、[X21X]プロトコルバッファを使用して.protoファイルで実行されます。 また、ペイロードメッセージの構造を説明するためにも使用されます。

4.1. 基本構成

サンプルHelloServiceHelloService.protoファイルを作成しましょう。 まず、いくつかの基本的な構成の詳細を追加します。

syntax = "proto3";
option java_multiple_files = true;
package org.baeldung.grpc;

最初の行は、このファイルで使用されている構文をコンパイラーに通知します。 デフォルトでは、コンパイラはすべてのJavaコードを単一のJavaファイルに生成します。 2行目はこの設定を上書きし、すべてが個別のファイルに生成されます。

最後に、生成されたJavaクラスに使用するパッケージを指定します。

4.2. メッセージ構造の定義

次に、メッセージを定義します。

message HelloRequest {
    string firstName = 1;
    string lastName = 2;
}

これは、要求ペイロードを定義します。 ここでは、メッセージに含まれる各属性がそのタイプとともに定義されています。

タグと呼ばれる一意の番号を各属性に割り当てる必要があります。 このタグは、属性名を使用する代わりに、属性を表すためにプロトコルバッファによって使用されます。

したがって、属性名 firstName を毎回渡すJSONとは異なり、プロトコルバッファは数値1を使用してfirstNameを表します。 応答ペイロードの定義は、要求に似ています。

複数のメッセージタイプで同じタグを使用できることに注意してください。

message HelloResponse {
    string greeting = 1;
}

4.3. サービス契約の定義

最後に、サービス契約を定義しましょう。 HelloService に対して、 hello()操作を定義します。

service HelloService {
    rpc hello(HelloRequest) returns (HelloResponse);
}

hello()演算は単項要求を受け入れ、単項応答を返します。 gRPCは、リクエストとレスポンスの前にstreamキーワードを付けることでストリーミングもサポートしています。

5. コードの生成

次に、HelloService.protoファイルをプロトコルバッファコンパイラprotocに渡して、Javaファイルを生成します。 これをトリガーする方法は複数あります。

5.1. プロトコルバッファコンパイラの使用

まず、ProtocolBufferCompilerが必要です。 ここで利用可能な多くのコンパイル済みバイナリから選択できます。

さらに、 gRPCJavaCodegenプラグインを入手する必要があります。

最後に、次のコマンドを使用してコードを生成できます。

protoc --plugin=protoc-gen-grpc-java=$PATH_TO_PLUGIN -I=$SRC_DIR 
  --java_out=$DST_DIR --grpc-java_out=$DST_DIR $SRC_DIR/HelloService.proto

5.2. Mavenプラグインの使用

開発者は、コード生成をビルドシステムと緊密に統合する必要があります。 gRPCは、Mavenビルドシステムにprotobuf-maven-pluginを提供します。

<build>
  <extensions>
    <extension>
      <groupId>kr.motd.maven</groupId>
      <artifactId>os-maven-plugin</artifactId>
      <version>1.6.1</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.3.0:exe:${os.detected.classifier}
        </protocArtifact>
        <pluginId>grpc-java</pluginId>
        <pluginArtifact>
          io.grpc:protoc-gen-grpc-java:1.4.0:exe:${os.detected.classifier}
        </pluginArtifact>
      </configuration>
      <executions>
        <execution>
          <goals>
            <goal>compile</goal>
            <goal>compile-custom</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

os-maven-plugin 拡張機能/プラグインは、 $ {os.detected.classifier} のような、プラットフォームに依存するさまざまな便利なプロジェクトプロパティを生成します。

6. サーバーの作成

コード生成に使用する方法に関係なく、次のキーファイルが生成されます。

  • HelloRequest.java –にはHelloRequestタイプ定義が含まれています
  • HelloResponse.java –これにはHelleResponseタイプ定義が含まれます
  • HelloServiceImplBase.java – これには、サービスインターフェイスで定義したすべての操作の実装を提供する抽象クラスHelloServiceImplBaseが含まれます

6.1. サービス基本クラスのオーバーライド

抽象クラスHelloServiceImplBaseのデフォルトの実装は、メソッドが実装されていないことを示すランタイム例外 io.grpc.StatusRuntimeExceptionをスローすることです。

このクラスを拡張し、サービス定義に記載されている hello()メソッドをオーバーライドします。

public class HelloServiceImpl extends HelloServiceImplBase {

    @Override
    public void hello(
      HelloRequest request, StreamObserver<HelloResponse> responseObserver) {

        String greeting = new StringBuilder()
          .append("Hello, ")
          .append(request.getFirstName())
          .append(" ")
          .append(request.getLastName())
          .toString();

        HelloResponse response = HelloResponse.newBuilder()
          .setGreeting(greeting)
          .build();

        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}

hello()の署名を HellService.proto ファイルに書き込んだ署名と比較すると、HelloResponseが返されないことがわかります。 代わりに、2番目の引数を次のように取ります StreamObserver 、これは応答オブザーバーであり、サーバーがその応答で呼び出すためのコールバックです。

このようにして、クライアントはブロッキングコールまたは非ブロッキングコールを行うオプションを取得します。

gRPCはビルダーを使用してオブジェクトを作成します。 HelloResponse.newBuilder()を使用し、挨拶テキストを設定してHelloResponseオブジェクトを作成します。 このオブジェクトをresponseObserverのonNext()メソッドに設定して、クライアントに送信します。

最後に、 onCompleted()を呼び出して、RPCの処理が終了したことを指定する必要があります。そうしないと、接続がハングし、クライアントはさらに情報が届くのを待つだけです。

6.2. Grpcサーバーの実行

次に、着信リクエストをリッスンするためにgRPCサーバーを起動する必要があります。

public class GrpcServer {
    public static void main(String[] args) {
        Server server = ServerBuilder
          .forPort(8080)
          .addService(new HelloServiceImpl()).build();

        server.start();
        server.awaitTermination();
    }
}

ここでも、ビルダーを使用してポート8080にgRPCサーバーを作成し、定義したHelloServiceImplサービスを追加します。 start()はサーバーを起動します。 この例では、 awaitTermination()を呼び出して、サーバーをフォアグラウンドで実行し続け、プロンプトをブロックします。

7. クライアントの作成

gRPCは、接続、接続プーリング、負荷分散などの基礎となる詳細を抽象化するチャネル構造を提供します。

ManagedChannelBuilderを使用してチャネルを作成します。 ここでは、サーバーのアドレスとポートを指定します。

暗号化なしのプレーンテキストを使用します。

public class GrpcClient {
    public static void main(String[] args) {
        ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 8080)
          .usePlaintext()
          .build();

        HelloServiceGrpc.HelloServiceBlockingStub stub 
          = HelloServiceGrpc.newBlockingStub(channel);

        HelloResponse helloResponse = stub.hello(HelloRequest.newBuilder()
          .setFirstName("Baeldung")
          .setLastName("gRPC")
          .build());

        channel.shutdown();
    }
}

次に、 hello()への実際のリモート呼び出しを行うために使用するスタブを作成する必要があります。 スタブは、クライアントがサーバーと対話するための主要な方法です。自動生成されたスタブを使用する場合、スタブクラスにはチャネルをラップするためのコンストラクターがあります。

ここでは、ブロッキング/同期スタブを使用して、RPC呼び出しがサーバーの応答を待機し、応答を返すか、例外を発生させるようにしています。 gRPCによって提供されるスタブには他に2つのタイプがあり、非ブロッキング/非同期呼び出しを容易にします。

最後に、 hello()RPC呼び出しを行う時間です。 ここで、HelloRequestを渡します。 自動生成されたセッターを使用して、HelloRequestオブジェクトのfirstNamelastName属性を設定できます。

サーバーから返されたHelloResponseオブジェクトが返されます。

8. 結論

このチュートリアルでは、サービスの定義に焦点を当て、gRPCにすべてのボイラープレートコードを処理させることで、gRPCを使用して2つのサービス間の通信の開発を容易にする方法を説明しました。

いつものように、ソースはGitHubにあります。