tokuhirom's Blog

armeria で grpc するサンプルコードをかいた

https://github.com/tokuhirom/armeria-sample line が出している rpc フレームワークであるところの armeria が最新版で grpc をサポートしたとのことで、実際どんなもんか試してみた。

試すのは簡単。build.gradle に protobuf プラグインを追加。

buildscript {
    repositories {
        mavenCentral()
        maven { url "https://plugins.gradle.org/m2/" }
    }
    dependencies {
        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.0'
    }
}

apply plugin: 'com.google.protobuf'

protobuf {
    protoc {
        // The version of protoc must match protobuf-java. If you don't depend on
        // protobuf-java directly, you will be transitively depending on the
        // protobuf-java version that grpc depends on.
        artifact = "com.google.protobuf:protoc:3.0.0"
    }
    plugins {
        grpc {
            artifact = 'io.grpc:protoc-gen-grpc-java:1.0.0'
        }
    }
    generateProtoTasks {
        all()*.plugins {
            grpc {}
        }
    }
}

idea {
    module {
        sourceDirs += file("${protobuf.generatedFilesBaseDir}/main/java");
        // If you have additional sourceSets and/or codegen plugins, add all of them
        sourceDirs += file("${protobuf.generatedFilesBaseDir}/main/grpc");
    }
}

dependencies {
    ["grpc-core", "grpc-stub", "grpc-protobuf"].each { module ->
        compile "io.grpc:$module:1.0.0"
    }
}

src/main/proto/hi.proto とかに以下のように protobuf の定義を置く。

syntax = "proto3";

package hi;

option java_multiple_files = true;
option java_package = "com.example.grpc";
option java_outer_classname = "HiProto";
option objc_class_prefix = "HI";

message MyStringValue {
    string value = 1;
}

service Hi {
    rpc Hello (MyStringValue) returns (MyStringValue) {
    }
}

で、./gradlew generateprotoで java コードが生成されるのでそれを継承したクラスを実装する。src/main/java/com/example/grpc/HiService.java 使い勝手は thrift と大差ない。

package com.example.grpc;

import io.grpc.stub.StreamObserver;

public class HiService extends HiGrpc.HiImplBase {
    @Override
    public void hello(MyStringValue request, StreamObserver<MyStringValue> responseObserver) {
        responseObserver.onNext(MyStringValue.newBuilder()
                .setValue("Hi, " + request.getValue() + "!")
                .build());
        responseObserver.onCompleted();
    }
}
```

最後に armeria サーバーの起動部分だけ実装すれば完成。簡単。

```
public class MyApp {
    public static void main(String[] args) {
        ServerBuilder sb = new ServerBuilder();
        sb.port(8080, SessionProtocol.HTTP);
        GrpcService grpcService = new GrpcServiceBuilder()
                .addService(new HiService())
                .build();
        sb.serviceUnder(
                "/",
                grpcService.decorate(LoggingService::new));
        Server server = sb.build();
        server.start();
    }
}
```

# gRPC 採用した場合のメリット

もともと armeria は thrift 用のサーバーなわけだけど、gRPC を採用した場合には以下のような恩恵を得られます。

 * Full support for request/response streaming
 * Protocol buffer generated code can be a nice change
 * Efficient clients on a variety of platforms (Android, iOS, Go, etc)

一方で、grpc 使うと DocService 使えないとか JSON で利用できないとかそういうデメリットも現状あるようだ

# armeria の上で gRPC 動かした場合のメリット

一方、gRPC 使ってるユーザーが armeria の上で gRPC を動かすメリットとしては以下のようなことがあるとのこと

 * Support for HTTP1 (not verified, will probably require some followup work). HTTP1 should open GRPC to the browser and work better with Cloud load balancers that generally translate HTTP2 -> HTTP1
 * Once implemented, DocService and Grpc on the same server
 * And any other servers they feel like having on the same server since armeria's flexible that way :)

とはいえ、現状だと grpc のクライアントって直接 HTTP で話せるわけでもないし HTTP1 サポートしたいというモチベーションも薄いかなあ。protobuf の方がネックになるし。あと DocService も grpc に対応してない様子。

DocService に対応して、さらに JSON エンドポイントが自動で生えるぐらいまでなったらぜひ使いたい。 https://github.com/grpc-ecosystem/grpc-gateway 的な感じで、` GrpcJsonServiceBuilder.service(myService).build()` とかで JSON  API 提供できる、とかだと嬉しい。

# まとめ
今後に期待。