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は使いまわしできるのかとか、並列で呼び出す場合にはどのように使うのか、、など、まだまだわからないところがあるので、今後もう少し調べてみようと思います。
これ読むといいよ!!とかありましたら、ぜひ教えてください。