gRPCをSpring Bootで利用する
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つの通信方式を実際に叩いて結果を見てみます。
1$ grpcurl --plaintext -d '{"name": "Kanna"}' localhost:6565 com.example.Greeter/SayHelloUnary
2{
3"message": "Hello,Kanna"
4}
1$ grpcurl --plaintext -d '{"name": "Kanna"}' localhost:6565 com.example.Greeter/SayHelloServerStreaming
2{
3"message": "Hello, Kanna"
4}
5{
6"message": "Hello, Kanna"
7}
1$ grpcurl --plaintext -d '{"name": "Kanna"}' localhost:6565 com.example.Greeter/SayHelloClientStreaming
2{
3"message": "Hello, [Kanna]"
4}
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の違いなどを感じていきたい。
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ
グンマー帝国から密出国してきてるブタ。 東京人多い...