gRPCとRESTの比較をしてみたかったので、まずはgRPCを触ってみました。
Srping Bootだと、grpc-spring-boot-starterを使うと簡単にgRPCのサーバが実装できます。すばらしい。
build.gradle
build.gradle は下記のような感じです。Spring BootのStarterで作成したものに、gRPCに必要なものを付け加えています。 参考にしたのは、grpc-spring-boot-starter のサンプルです。
buildscript {
ext { springBootVersion = '2.1.2.RELEASE' }
repositories { mavenCentral() }
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath('com.google.protobuf:protobuf-gradle-plugin:0.8.8')
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'com.google.protobuf'
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories { mavenCentral() }
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'io.github.lognet:grpc-spring-boot-starter:3.1.0'
compileOnly 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.assertj:assertj-core:3.11.1'
}
protobuf {
protoc { artifact = 'com.google.protobuf:protoc:3.5.1' }
plugins {
grpc { artifact = "io.grpc:protoc-gen-grpc-java:1.18.0" }
}
generateProtoTasks {
ofSourceSet('main').each { task ->
task.builtins {
java{ outputSubDir = 'protogen' }
}
task.plugins {
grpc { outputSubDir = 'protogen' }
}
}
}
generatedFilesBaseDir = "$projectDir/src/"
}
sourceSets {
main {
java { srcDir 'src/main/protogen' }
}
}
task cleanProtoGen{
doFirst{
delete("$projectDir/src/main/protogen")
}
}
clean.dependsOn cleanProtoGen
proto
proto の定義は、とりあえずgRPCの公式サイトにあるサンプルと同じで。
syntax = "proto3"; option java_package = "com.example.grpc"; // The greeting service definition. service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings message HelloReply { string message = 1; }
Gradle の generateProto タスクを実行すると、上記定義を元にJavaのコードが生成されます。
@GRpcService の実装
あとは@GRpcServiceを付与したサービスクラスを実装するだけです。
package com.example.grpc; import org.lognet.springboot.grpc.GRpcService; import com.example.grpc.GreeterOuterClass.HelloReply; import com.example.grpc.GreeterOuterClass.HelloRequest; import io.grpc.stub.StreamObserver; @GRpcService public class GreeterService extends GreeterGrpc.GreeterImplBase { @Override public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) { HelloReply reply = HelloReply.newBuilder() .setMessage("Hello " + request.getName()) .build(); responseObserver.onNext(reply); responseObserver.onCompleted(); } }
クライアントのコード
クライアントのコードは下記のような感じで。(Spring Bootのテストコードとして書いてます)
package com.example.grpc; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; import org.junit.runner.RunWith; import org.lognet.springboot.grpc.context.LocalRunningGrpcPort; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import com.example.grpc.GreeterOuterClass.HelloReply; import com.example.grpc.GreeterOuterClass.HelloRequest; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; @RunWith(SpringRunner.class) @SpringBootTest public class GreeterServiceTest { @LocalRunningGrpcPort private int runningPort; @Test public void sayHello() { ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", runningPort) .usePlaintext() .build(); GreeterGrpc.GreeterBlockingStub stub = GreeterGrpc.newBlockingStub(channel); String name = "Taro"; HelloRequest request = HelloRequest.newBuilder() .setName(name) .build(); HelloReply reply = stub.sayHello(request); assertThat(reply.getMessage()).isEqualTo("Hello " + name); } }
おわりに
ちょっと試してみる分には、すごく簡単に実行できました。
コード全体は、下記のプロジェクトになります。
Channelは使いまわしできるのかとか、並列で呼び出す場合にはどのように使うのか、、など、まだまだわからないところがあるので、今後もう少し調べてみようと思います。
これ読むといいよ!!とかありましたら、ぜひ教えてください。