• トップ
  • ブログ一覧
  • gRPCをSpring Bootで利用する
  • gRPCをSpring Bootで利用する

    たかやん(エンジニア)たかやん(エンジニア)
    2023.08.22

    IT技術

    はじめに

    業務でSpringBootでREST APIの開発を行っていたのですが、マイクロサービス化するに当たりgRPCを利用して開発することになりました。そこで個人勉強として簡単なサービスを作ったので記録します。

    SpringBootでgRPCを実装して、gRPCの4つの通信方式まで実際にやってみます。この記事の内容を上から行うとspring boot+gRPCで動く環境が作れます。

    プロジェクトの作成

    https://start.spring.io/ でプロジェクトを作成します。依存関係の追加は不要です。ProjectはMavenを選択してください。

    ダウンロード&解凍を行い、pom.xml を修正します。

    pom.xmlの修正

    ダウンロードして解凍したプロジェクトにある、pom.xml を修正してgRPCとSpringBootを利用できるようにしていきます。

    最初はgRPC対応です。dependencyを追加してライブライのダウンロードを行います。

    1<dependency>
    2  <groupId>io.grpc</groupId>
    3  <artifactId>grpc-netty-shaded</artifactId>
    4  <version>1.57.0</version>
    5  <scope>runtime</scope>
    6</dependency>
    7<dependency>
    8  <groupId>io.grpc</groupId>
    9  <artifactId>grpc-protobuf</artifactId>
    10  <version>1.57.0</version>
    11</dependency>
    12<dependency>
    13  <groupId>io.grpc</groupId>
    14  <artifactId>grpc-stub</artifactId>
    15  <version>1.57.0</version>
    16</dependency>
    17<dependency> <!-- necessary for Java 9+ -->
    18  <groupId>org.apache.tomcat</groupId>
    19  <artifactId>annotations-api</artifactId>
    20  <version>6.0.53</version>
    21  <scope>provided</scope>
    22</dependency>

    pluginの追加

    1<build>
    2  <extensions>
    3    <extension>
    4      <groupId>kr.motd.maven</groupId>
    5      <artifactId>os-maven-plugin</artifactId>
    6      <version>1.7.1</version>
    7    </extension>
    8  </extensions>
    9  <plugins>
    10    <plugin>
    11      <groupId>org.xolstice.maven.plugins</groupId>
    12      <artifactId>protobuf-maven-plugin</artifactId>
    13      <version>0.6.1</version>
    14      <configuration>
    15        <protocArtifact>com.google.protobuf:protoc:3.23.4:exe:${os.detected.classifier}</protocArtifact>
    16        <pluginId>grpc-java</pluginId>
    17        <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.57.0:exe:${os.detected.classifier}</pluginArtifact>
    18      </configuration>
    19      <executions>
    20        <execution>
    21          <goals>
    22            <goal>compile</goal>
    23            <goal>compile-custom</goal>
    24          </goals>
    25        </execution>
    26      </executions>
    27    </plugin>
    28  </plugins>
    29</build>

    これでjavaでgPRCが使えるようになりました。次にspring bootで利用できるようにします。

    1<dependency>
    2	<groupId>io.github.lognet</groupId>
    3	<artifactId>grpc-spring-boot-starter</artifactId>
    4	<version>5.1.3</version>
    5</dependency>

    これでpom.xmlは準備完了です。

    protoファイルの作成

    src/main配下にprotoフォルダを作成し、Greeter.protoファイルを作成して下記の内容を記述。

    1syntax = "proto3";
    2
    3package com.example;
    4
    5service Greeter {
    6  // Unary RPC (単一リクエスト, 単一レスポンス)
    7  rpc SayHelloUnary (GreeterRequest) returns (GreeterReply) {}
    8  // Server streaming RPC (単一リクエスト, 複数レスポンス)
    9  rpc SayHelloServerStreaming (GreeterRequest) returns (stream GreeterReply) {}
    10  // Client streaming RPC (複数リクエスト, 単一レスポンス)
    11  rpc SayHelloClientStreaming (stream GreeterRequest) returns (GreeterReply) {}
    12  // Bidirectional streaming RPC (複数リクエスト, 複数レスポンス)
    13  rpc SayHelloBidirectionalStreaming (stream GreeterRequest) returns (stream GreeterReply) {}
    14}
    15
    16// ユーザ名を含むリクエスト
    17message GreeterRequest {
    18  string name = 1;
    19}
    20
    21// Hello,を含むレスポンス
    22message GreeterReply {
    23  string message = 1;
    24}

    コメントを入れているのでわかるかと思いますが、複数のリクエスト/レスポンスの場合はstreamをつけることで可能です。

    gRPCサーバ

    gRPCのリクエストを受け付けるアプリケーションサーバを実装します。

    src/main配下にsrviceフォルダを作成し、GreeterImpl.javaファイルを作成して下記の内容を記述。

    1@GRpcService
    2public class GreeterImpl extends GreeterGrpc.GreeterImplBase {
    3
    4  /*
    5   * Unary RPC (単一リクエスト, 単一レスポンス)
    6   *
    7   * @param request          request
    8   * @param responseObserver responseObserver
    9   */
    10  @Override
    11  public void sayHelloUnary(GreeterRequest request, StreamObserver<GreeterReply> responseObserver) {
    12
    13    responseObserver.onNext(
    14        GreeterReply.newBuilder().setMessage("Hello," + request.getName()).build());
    15    responseObserver.onCompleted();
    16  }
    17
    18  /**
    19   * Server streaming RPC (単一リクエスト, 複数レスポンス)
    20   *
    21   * @param request          request
    22   * @param responseObserver responseObserver
    23   */
    24  @Override
    25  public void sayHelloServerStreaming(GreeterRequest request,
    26      StreamObserver<GreeterReply> responseObserver) {
    27    GreeterReply greeterReply = GreeterReply.newBuilder().setMessage("Hello, " + request.getName())
    28        .build();
    29    responseObserver.onNext(greeterReply);
    30    responseObserver.onNext(greeterReply);
    31    responseObserver.onCompleted();
    32  }
    33
    34  /**
    35   * Client streaming RPC (複数リクエスト, 単一レスポンス)
    36   *
    37   * @param responseObserver responseObserver
    38   * @return StreamObserver<GreeterRequest> StreamObserver<GreeterRequest>
    39   */
    40  @Override
    41  public StreamObserver<GreeterRequest> sayHelloClientStreaming(
    42      StreamObserver<GreeterReply> responseObserver) {
    43
    44    List<String> requests = new ArrayList<>();
    45    return new StreamObserver<>() {
    46      @Override
    47      public void onNext(GreeterRequest request) {
    48        requests.add(request.getName());
    49      }
    50
    51      @Override
    52      public void onError(Throwable t) {
    53      }
    54
    55      @Override
    56      public void onCompleted() {
    57        GreeterReply greeterReply = GreeterReply.newBuilder().setMessage("Hello, " + requests)
    58            .build();
    59        responseObserver.onNext(greeterReply);
    60        responseObserver.onCompleted();
    61      }
    62    };
    63  }
    64
    65  /**
    66   * Bidirectional streaming RPC (複数リクエスト, 複数レスポンス)
    67   *
    68   * @param responseObserver responseObserver
    69   * @return StreamObserver<GreeterRequest> StreamObserver<GreeterRequest>
    70   */
    71  @Override
    72  public StreamObserver<GreeterRequest> sayHelloBidirectionalStreaming(
    73      StreamObserver<GreeterReply> responseObserver) {
    74    return new StreamObserver<>() {
    75      @Override
    76      public void onNext(GreeterRequest request) {
    77        GreeterReply greeterReply = GreeterReply.newBuilder()
    78            .setMessage("Hello " + request.getName()).build();
    79        responseObserver.onNext(greeterReply);
    80        responseObserver.onNext(greeterReply);
    81      }
    82
    83      @Override
    84      public void onError(Throwable t) {
    85        //
    86      }
    87
    88      @Override
    89      public void onCompleted() {
    90        responseObserver.onCompleted();
    91      }
    92    };
    93  }
    94}

    これで準備完了です。次に実際にリクエストしてみます。

    リクエストしてみる

    gRPCを呼び出すためにgrpcurlをbrewでインストールします

    1brew install grpcurl

    インストールが完了したらサービスのメソッドを表示してみます。

    1$ grpcurl --plaintext localhost:6565 list com.example.Greeter 
    2com.example.Greeter.SayHelloBidirectionalStreaming
    3com.example.Greeter.SayHelloClientStreaming
    4com.example.Greeter.SayHelloServerStreaming
    5com.example.Greeter.SayHelloUnary

    -plaintext: TLSを利用していない場合に利用するオプション

    localhost:6565: gRPCサーバーのホスト名とポート番号を指定します

    com.example.Greeter: パッケージ名

    メソッドを叩く

    最初に軽く触れた4つの通信方式を実際に叩いて結果を見てみます。

    SayHelloUnary: Unary RPC (単一リクエスト, 単一レスポンス)
    1$ grpcurl --plaintext -d '{"name": "Kanna"}' localhost:6565 com.example.Greeter/SayHelloUnary
    2{
    3"message": "Hello,Kanna"
    4}
    SayHelloServerStreaming: Server streaming RPC (単一リクエスト, 複数レスポンス)
    1$ grpcurl --plaintext -d '{"name": "Kanna"}' localhost:6565 com.example.Greeter/SayHelloServerStreaming
    2{
    3"message": "Hello, Kanna"
    4}
    5{
    6"message": "Hello, Kanna"
    7}
    SayHelloClientStreaming: Client streaming RPC (複数リクエスト, 単一レスポンス)
    1$ grpcurl --plaintext -d '{"name": "Kanna"}' localhost:6565 com.example.Greeter/SayHelloClientStreaming
    2{
    3"message": "Hello, [Kanna]"
    4}
    SayHelloBidirectionalStreaming:Bidirectional streaming RPC (複数リクエスト, 複数レスポンス)
    1$ grpcurl --plaintext -d '{"name": "Kanna"}' localhost:6565 com.example.Greeter/SayHelloBidirectionalStreaming
    2{
    3"message": "Hello Kanna"
    4}
    5{
    6"message": "Hello Kanna"
    7}

    それぞれの通信方式の確認ができましたね!

    まとめ

    現状、私が業務をするうえで触った内容になります。今後は実業務で利用するバリーションやエラーハンドリングなどもさわりたい。ライブラリ入れるだけで実装できそうなのは確認した。

    これだけではマイクロサービスへのメリットなどは一切感じることができてない。リアルタイムストリーミング通信をする際はgRPCのほうが実装が簡単だと思ったぐらい。もっと機能追加してgRPCとREST APIの違いなどを感じていきたい。

    たかやん(エンジニア)

    たかやん(エンジニア)

    おすすめ記事