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

    はじめに

    業務で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の違いなどを感じていきたい。

    ライトコードでは、エンジニアを積極採用中!

    ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。

    採用情報へ

    たかやん(エンジニア)
    たかやん(エンジニア)
    Show more...

    おすすめ記事

    エンジニア大募集中!

    ライトコードでは、エンジニアを積極採用中です。

    特に、WEBエンジニアとモバイルエンジニアは是非ご応募お待ちしております!

    また、フリーランスエンジニア様も大募集中です。

    background