Spring boot で gRPC の server reflection を行う


目的

Spring Boot + kotlin + gRPC でアプリケーションを実装する上で簡易な動作確認ができるようにgrpcurlというツールを使っています.
このツールはプロトコルバッファの記述子ファイルを必要とするため定義ファイルからそれを作成し使用しています.

しかしこれですとプロトコルバッファの定義ファイルが変更されるたび記述子ファイルの更新もしなければならず面倒なためserver reflectionを使い楽をすることにしました.

grpcurlについてはgithubを参照してください.
https://github.com/fullstorydev/grpcurl

server reflectionとは

この記事 を参考にさせていただきました.

サーバーリフレクションはクライアントのコマンドラインからgRPCの定義の取得あるいは実行を可能にするgRPCサーバの拡張機能。サーバーリフレクションによってcurlライクなことがgRPCサーバでも実現できる。

このことからサーバ側からgRPCの定義の提供を受けることで grpcurlが必要としているプロトコルバッファの記述子ファイルがなくても通信ができるようになるようです.

環境

  • Spring Boot 2.0.0
  • grpc-spring-boot-starter 2.2.0
  • kotlin 1.2.20
  • gradle 4.5.1

実装

https://github.com/grpc/grpc-java/blob/master/documentation/server-reflection-tutorial.md を参考にします.

build.gradle

必要なパッケージを追加します.

 dependencies {
     compile "io.grpc:grpc-netty:${grpcVersion}"
     compile "io.grpc:grpc-protobuf:${grpcVersion}"
     compile "io.grpc:grpc-stub:${grpcVersion}"
+    compile "io.grpc:grpc-services:${grpcVersion}"
     compile "org.lognet:grpc-spring-boot-starter:2.2.0"

interceptor

grpc-spring-boot-starterのドキュメント を読むと GRpcServerBuilderConfigurer を継承したクラスを用意すればいいようです.

package com.yamatomo.sample.grpcInterceptors

import io.grpc.ServerBuilder
import io.grpc.protobuf.services.ProtoReflectionService
import org.lognet.springboot.grpc.GRpcServerBuilderConfigurer
import org.springframework.stereotype.Component

@Component
class BuildConfigure: GRpcServerBuilderConfigurer() {
    override fun configure(serverBuilder: ServerBuilder<*>) {
        serverBuilder.addService(ProtoReflectionService.newInstance())
    }
}

あとは gradle bootRun で起動すればOKです. 記述子ファイルがなくとも grpcurl でレスポンスがうけとれるようになります.

$ grpcurl -plaintext localhost:6565 sample.ExampleService.GetTasks
{
  "task": [
    {
      "id": 1,
      "text": "hogehoge"
    },
    {
      "id": 2,
      "text": "foobar"
    }
  ]
}

開発環境のみ server reflection が有効になるようにする

あくまで動作確認が目的なので application.properties に server reflection の On/Off を定義し開発環境のみ有効にするコードにしてみます.

application.properties

grpc.interceptor.useReflection=false

application-development.properties

開発環境用の設定ファイルを用意します.

grpc.interceptor.useReflection=true

データクラス

application.properties をマッピングするデータクラスを作成します.

package com.yamatomo.sample.grpcInterceptors

import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.stereotype.Component

@Component
@ConfigurationProperties(prefix = "grpc.interceptor")
data class InterceptorProperties (
    var useReflection: Boolean = false
)

interceptor

先ほど実装したコードにデータクラスから On/Off を判定するように書き換えます.

 package com.yamatomo.sample.grpcInterceptors

 import io.grpc.ServerBuilder
 import io.grpc.protobuf.services.ProtoReflectionService
 import org.lognet.springboot.grpc.GRpcServerBuilderConfigurer
+import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.stereotype.Component

 @Component
 class BuildConfigure: GRpcServerBuilderConfigurer() {
+    @Autowired
+    lateinit var properties: InterceptorProperties

     override fun configure(serverBuilder: ServerBuilder<*>) {
+       if (!properties.useReflection) return

        serverBuilder.addService(ProtoReflectionService.newInstance())
     }
 }

bootRunのオプションでprofileをdevelopmentと指定して起動します.

$ gradlew bootRun -Dspring.profiles.active=development

まとめ

気軽に動作確認ができるのは大きいと思いました.

要件によっては grpc-gateway を導入しないケースでも動作確認のためだけに導入するといったことも減らせるのではないかなと思います.