gRPC はじめの一歩 in Java

f:id:Naotsugu:20190924235738p:plain



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"
    }
  }
}


このようなコードが生成されました。

f:id:Naotsugu:20190924235001p:plain

java_multiple_filesoption java_packagejava_outer_classname のオプションを書かない場合は以下のようになります。 f:id:Naotsugu:20190930204510p:plain

サーバコードの実装

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"
    }
  }
}



blog1.mammb.com



Hands-On Microservices with Spring Boot and Spring Cloud: Build and deploy Java microservices using Spring Cloud, Istio, and Kubernetes

Hands-On Microservices with Spring Boot and Spring Cloud: Build and deploy Java microservices using Spring Cloud, Istio, and Kubernetes

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