
gRPCをSpring Bootで利用する
2023.09.20
はじめに
業務で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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty-shaded</artifactId> <version>1.57.0</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>1.57.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>1.57.0</version> </dependency> <dependency> <!-- necessary for Java 9+ --> <groupId>org.apache.tomcat</groupId> <artifactId>annotations-api</artifactId> <version>6.0.53</version> <scope>provided</scope> </dependency> |
pluginの追加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.7.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.23.4:exe:${os.detected.classifier}</protocArtifact> <pluginId>grpc-java</pluginId> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.57.0:exe:${os.detected.classifier}</pluginArtifact> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> </plugins> </build> |
これでjavaでgPRCが使えるようになりました。次にspring bootで利用できるようにします。
1 2 3 4 5 | <dependency> <groupId>io.github.lognet</groupId> <artifactId>grpc-spring-boot-starter</artifactId> <version>5.1.3</version> </dependency> |
これでpom.xmlは準備完了です。
protoファイルの作成
src/main配下にprotoフォルダを作成し、Greeter.protoファイルを作成して下記の内容を記述。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | syntax = "proto3"; package com.example; service Greeter { // Unary RPC (単一リクエスト, 単一レスポンス) rpc SayHelloUnary (GreeterRequest) returns (GreeterReply) {} // Server streaming RPC (単一リクエスト, 複数レスポンス) rpc SayHelloServerStreaming (GreeterRequest) returns (stream GreeterReply) {} // Client streaming RPC (複数リクエスト, 単一レスポンス) rpc SayHelloClientStreaming (stream GreeterRequest) returns (GreeterReply) {} // Bidirectional streaming RPC (複数リクエスト, 複数レスポンス) rpc SayHelloBidirectionalStreaming (stream GreeterRequest) returns (stream GreeterReply) {} } // ユーザ名を含むリクエスト message GreeterRequest { string name = 1; } // Hello,を含むレスポンス message GreeterReply { string message = 1; } |
コメントを入れているのでわかるかと思いますが、複数のリクエスト/レスポンスの場合はstreamをつけることで可能です。
gRPCサーバ
gRPCのリクエストを受け付けるアプリケーションサーバを実装します。
src/main配下にsrviceフォルダを作成し、GreeterImpl.javaファイルを作成して下記の内容を記述。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | @GRpcService public class GreeterImpl extends GreeterGrpc.GreeterImplBase { /** * Unary RPC (単一リクエスト, 単一レスポンス) * * @param request request * @param responseObserver responseObserver */ @Override public void sayHelloUnary(GreeterRequest request, StreamObserver<GreeterReply> responseObserver) { responseObserver.onNext( GreeterReply.newBuilder().setMessage("Hello," + request.getName()).build()); responseObserver.onCompleted(); } /** * Server streaming RPC (単一リクエスト, 複数レスポンス) * * @param request request * @param responseObserver responseObserver */ @Override public void sayHelloServerStreaming(GreeterRequest request, StreamObserver<GreeterReply> responseObserver) { GreeterReply greeterReply = GreeterReply.newBuilder().setMessage("Hello, " + request.getName()) .build(); responseObserver.onNext(greeterReply); responseObserver.onNext(greeterReply); responseObserver.onCompleted(); } /** * Client streaming RPC (複数リクエスト, 単一レスポンス) * * @param responseObserver responseObserver * @return StreamObserver<GreeterRequest> StreamObserver<GreeterRequest> */ @Override public StreamObserver<GreeterRequest> sayHelloClientStreaming( StreamObserver<GreeterReply> responseObserver) { List<String> requests = new ArrayList<>(); return new StreamObserver<>() { @Override public void onNext(GreeterRequest request) { requests.add(request.getName()); } @Override public void onError(Throwable t) { } @Override public void onCompleted() { GreeterReply greeterReply = GreeterReply.newBuilder().setMessage("Hello, " + requests) .build(); responseObserver.onNext(greeterReply); responseObserver.onCompleted(); } }; } /** * Bidirectional streaming RPC (複数リクエスト, 複数レスポンス) * * @param responseObserver responseObserver * @return StreamObserver<GreeterRequest> StreamObserver<GreeterRequest> */ @Override public StreamObserver<GreeterRequest> sayHelloBidirectionalStreaming( StreamObserver<GreeterReply> responseObserver) { return new StreamObserver<>() { @Override public void onNext(GreeterRequest request) { GreeterReply greeterReply = GreeterReply.newBuilder() .setMessage("Hello " + request.getName()).build(); responseObserver.onNext(greeterReply); responseObserver.onNext(greeterReply); } @Override public void onError(Throwable t) { // } @Override public void onCompleted() { responseObserver.onCompleted(); } }; } } |
これで準備完了です。次に実際にリクエストしてみます。
リクエストしてみる
gRPCを呼び出すためにgrpcurlをbrewでインストールします
1 | brew install grpcurl |
インストールが完了したらサービスのメソッドを表示してみます。
1 2 3 4 5 | $ grpcurl --plaintext localhost:6565 list com.example.Greeter com.example.Greeter.SayHelloBidirectionalStreaming com.example.Greeter.SayHelloClientStreaming com.example.Greeter.SayHelloServerStreaming com.example.Greeter.SayHelloUnary |
-plaintext: TLSを利用していない場合に利用するオプション
localhost:6565: gRPCサーバーのホスト名とポート番号を指定します
com.example.Greeter: パッケージ名
メソッドを叩く
最初に軽く触れた4つの通信方式を実際に叩いて結果を見てみます。
1 2 3 4 | $ grpcurl --plaintext -d '{"name": "Kanna"}' localhost:6565 com.example.Greeter/SayHelloUnary { "message": "Hello,Kanna" } |
1 2 3 4 5 6 7 | $ grpcurl --plaintext -d '{"name": "Kanna"}' localhost:6565 com.example.Greeter/SayHelloServerStreaming { "message": "Hello, Kanna" } { "message": "Hello, Kanna" } |
1 2 3 4 | $ grpcurl --plaintext -d '{"name": "Kanna"}' localhost:6565 com.example.Greeter/SayHelloClientStreaming { "message": "Hello, [Kanna]" } |
1 2 3 4 5 6 7 | $ grpcurl --plaintext -d '{"name": "Kanna"}' localhost:6565 com.example.Greeter/SayHelloBidirectionalStreaming { "message": "Hello Kanna" } { "message": "Hello Kanna" } |
それぞれの通信方式の確認ができましたね!
まとめ
現状、私が業務をするうえで触った内容になります。今後は実業務で利用するバリーションやエラーハンドリングなどもさわりたい。ライブラリ入れるだけで実装できそうなのは確認した。
これだけではマイクロサービスへのメリットなどは一切感じることができてない。リアルタイムストリーミング通信をする際はgRPCのほうが実装が簡単だと思ったぐらい。もっと機能追加してgRPCとREST APIの違いなどを感じていきたい。
書いた人はこんな人

- グンマー帝国から密出国してきてるブタ。
東京人多い...
IT技術10月 26, 2023HSTS(HTTP Strict Transport Security)とは?
IT技術8月 22, 2023gRPCをSpring Bootで利用する
IT技術12月 14, 2021OAuthとは?なぜ必要?
エンジニアになろう!1月 25, 2021MVVMアーキテクチャで書籍管理アプリを作ってみた!