
gRPC とは
.protoによる IDL(Interface Description Language)からRPC用のソースを生成- REST と比べ API 仕様が規定しやすい
- Go、Java、Node、Python、Ruby、C#、 C++ など多くの言語に対応し、RPCは特定の言語に依存しない
- Protocol Buffers によるAPIコールで通信効率が良い
- HTTP/2による双方向ストリーミングに対応
など。マイクロサービスでの利用に吉。
プロジェクトの作成
Gradle でプロジェクトを作成します。
init タスクでプロジェクトの雛形を作成しましょう。
$ mkdir example-grpc $ cd example-grpc $ gradle init --type java-application
build.gradle を以下のように変更します。
plugins {
id 'java'
id 'application'
id 'com.google.protobuf' version '0.8.8'
}
repositories { jcenter() }
mainClassName = 'App'
sourceCompatibility = targetCompatibility = 1.8
dependencies {
implementation 'io.grpc:grpc-netty-shaded:1.23.0'
implementation 'io.grpc:grpc-protobuf:1.23.0'
implementation 'io.grpc:grpc-stub:1.23.0'
implementation 'javax.annotation:javax.annotation-api:1.3.2'
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.9.0"
}
plugins {
grpc {
artifact = 'io.grpc:protoc-gen-grpc-java:1.23.0'
}
}
generateProtoTasks {
all()*.plugins {
grpc {}
}
}
}
Gradle プラグイン com.google.protobuf を使います。
このプラグインにて、インターフェース定義ファイル .proto からRPC用のコードが自動生成されます。
プラグインの設定は protobuf ブロックにて行います。
Java9 以降の場合は javax.annotation-api の依存が必要になります。
.proto ファイルの作成
RPC のインターフェースを定義します。
helloworld.proto を作成します。
$ mkdir -p src/main/proto/ $ touch src/main/proto/helloworld.proto
helloworld.proto を以下のように定義します。
syntax = "proto3"; option java_multiple_files = true; option java_package = "example.grpc.helloworld"; option java_outer_classname = "HelloWorldProto"; package helloworld; service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; }
Greeter というサービスで SayHello というインターフェースを定義しました。
generateProto タスクで .proto ファイルをコンパイルしてコード生成します。
$ ./gradlew generateProto
build/generated/source/proto 以下にコードが吐かれます。
IDE で生成されたコードをソースセットに加えるには build.gradle に以下を追加します。
sourceSets {
main {
java {
srcDirs "$buildDir/generated/source/proto/main/grpc"
srcDirs "$buildDir/generated/source/proto/main/java"
}
}
}
このようなコードが生成されました。
java_multiple_files、option java_package、java_outer_classname のオプションを書かない場合は以下のようになります。
サーバコードの実装
GreeterImplBase というクラスを継承して sayHello メソッドを定義します。
package example; import example.grpc.helloworld.GreeterGrpc; import example.grpc.helloworld.HelloReply; import example.grpc.helloworld.HelloRequest; import io.grpc.stub.StreamObserver; public class GreeterImpl extends GreeterGrpc.GreeterImplBase { @Override public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) { HelloReply reply = HelloReply.newBuilder() .setMessage("Hello " + req.getName()).build(); responseObserver.onNext(reply); responseObserver.onCompleted(); } }
HelloRequest というリクエストを取り、レスポンスは StreamObserver 経由でレスポンスを返却します。
サーバは ServerBuilder を使って実装します。
import example.GreeterImpl; import io.grpc.Server; import io.grpc.ServerBuilder; public class App { public static void main(String[] args) throws Exception { Server server = ServerBuilder.forPort(50051) .addService(new GreeterImpl()) .build() .start(); Runtime.getRuntime().addShutdownHook(new Thread(server::shutdown)); server.awaitTermination(); } }
先程作成した GreeterImpl をサービスとして登録します。
クライアントコードの実装
クライアントはテストコードから実行することにします。
JUnit の依存を build.gradle に追加しておきます。
dependencies {
// ...
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.5.2'
}
test {
useJUnitPlatform()
testLogging.showStandardStreams = true
}
クライアントコードは、こちらも生成された GreeterBlockingStub を使い、以下のような実装になります。
import example.grpc.helloworld.GreeterGrpc; import example.grpc.helloworld.HelloReply; import example.grpc.helloworld.HelloRequest; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class AppTest { @Test void testApp() { ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051) .usePlaintext() .build(); GreeterGrpc.GreeterBlockingStub blockingStub = GreeterGrpc.newBlockingStub(channel); HelloRequest request = HelloRequest.newBuilder().setName("Thom").build(); HelloReply response = blockingStub.sayHello(request); System.out.println(response.getMessage()); assertEquals(response.getMessage(), "Hello Thom"); } }
実行
application プラグインを入れているので、サーバは以下で実行します。
$ ./gradlew run
サーバが起動したら、テストを実行します。
$ ./gradlew test
AppTest > testApp() STANDARD_OUT
Hello Thom
API の呼び出しができました。
最後に、今回使ったbuild.gradle は以下のようになりました。
plugins {
id 'java'
id 'application'
id 'com.google.protobuf' version '0.8.8'
}
repositories {
jcenter()
}
mainClassName = 'App'
sourceCompatibility = targetCompatibility = 1.8
dependencies {
implementation 'io.grpc:grpc-netty-shaded:1.23.0'
implementation 'io.grpc:grpc-protobuf:1.23.0'
implementation 'io.grpc:grpc-stub:1.23.0'
implementation 'javax.annotation:javax.annotation-api:1.3.2'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.5.2'
}
test {
useJUnitPlatform()
testLogging.showStandardStreams = true
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.9.0"
}
plugins {
grpc {
artifact = 'io.grpc:protoc-gen-grpc-java:1.23.0'
}
}
generateProtoTasks {
all()*.plugins {
grpc {}
}
}
}
// for IDE
sourceSets {
main {
java {
srcDirs "$buildDir/generated/source/proto/main/grpc"
srcDirs "$buildDir/generated/source/proto/main/java"
}
}
}

- 作者:Magnus Larsson
- 出版社/メーカー: Packt Publishing
- 発売日: 2019/09/20
- メディア: ペーパーバック