Spring5.0 + Kotlinで1つのjarにHTTPサーバーとgRPCサーバーを相乗りさせてみた
Spring Boot 2.0.0 M1がリリースされました。以前のエントリで試した当時は 2.0.0.BUILD-SNAPSHOTでありHTTPサーバーが起動している状態でgRPCクライアントを動かすとエラーになっていた。
2.0.0 M1のリリースに伴いHTTPサーバーとgRPCサーバーが1つのjarに相乗りできるようになっているか確認するのが今回のモチベーション。
次のようなアプリケーション構成を実現したい。
 
 
- API Serverは- HTTP1.1のリクエストのルーティングと- gRPCサーバーのエンドポイントを提供する
- API Serverに届いたリクエストはBackend Server向けのgRPCクライアントからBackend Serverにリクエストする
- API ServerとBackend Server間の通信はgRPCで行う
- エンドポイントにはHTTP1.1のリクエストのルーティングとgRPCサーバーの2つの通信方式を用意する
- すべてのアプリケーションはSpring Boot 2.0.0 M1の上で動く
- Introducing Kotlin support in Spring Framework 5.0のエントリにあるようなSpring5.0で提供される新しい機能をつかう
ここからは試した過程での気づきなどをアウトプットしていく。
CommandLineRunnerでgRPCサーバーを起動する
HTTPルーティングが動いている状態で CommandLineRunnerをつかいgRPCサーバーを次のように起動させた。
|  |  | 
Spring Boot 2.0.0 M1で試したところ問題なく起動した!(2.0.0.BUILD-SNAPSHOTではエラーになっていたところ)
2.0.0 M1では300以上のissueやプルリクエストがマージされたので、その中のどれかで解消されたということだろう。
grpc-javaの 1.3.0はエラーになる
Spring Bootには直接関係はないところであるが最新のgrpc-javaの1.3.0を使うとエラーになった。
| 1 2 3 4 5 6 7 8 | java.lang.NoClassDefFoundError: io/netty/handler/codec/http2/internal/hpack/Decoder
        at io.grpc.netty.GrpcHttp2HeadersDecoder.<init>(GrpcHttp2HeadersDecoder.java:85) ~[grpc-netty-1.2.0.jar:1.2.0]
        at io.grpc.netty.GrpcHttp2HeadersDecoder$GrpcHttp2ServerHeadersDecoder.<init>(GrpcHttp2HeadersDecoder.java:135) ~[grpc-netty-1.2.0.jar:1.2.0]
        at io.grpc.netty.NettyServerHandler.newHandler(NettyServerHandler.java:109) ~[grpc-netty-1.2.0.jar:1.2.0]
        at io.grpc.netty.NettyServerTransport.createHandler(NettyServerTransport.java:132) ~[grpc-netty-1.2.0.jar:1.2.0]
        at io.grpc.netty.NettyServerTransport.start(NettyServerTransport.java:77) ~[grpc-netty-1.2.0.jar:1.2.0]
        at io.grpc.netty.NettyServer$1.initChannel(NettyServer.java:141) ~[grpc-netty-1.2.0.jar:1.2.0]
・・・ | 
Netty 4.1.9ではio.netty.handler.codec.http2.internal.hpack.Decoderが削除されているのが原因。
gradleは次のようにgrpc-nettyのみ1.4.0-SNAPSHOTのバージョンを指定することで解消できる。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | buildscript {
    ext.grpc_version = "1.3.0"
    ext.grpc_version_snapshot = "1.4.0-SNAPSHOT"
    repositories {
        mavenCentral()
        maven { url "https://repo.spring.io/milestone" }
        maven { url 'http://repo.spring.io/plugins-release' }
        maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
    }
}
・・・
dependencies {
・・・
    // grpc
    compile "io.grpc:grpc-netty:${grpc_version_snapshot}"
    compile "io.grpc:grpc-protobuf:${grpc_version}"
    compile "io.grpc:grpc-stub:${grpc_version}"
    compile "io.grpc:grpc-okhttp:${grpc_version}"
    compile "com.google.api.grpc:googleapis-common-protos:0.0.3"
・・・
} | 
BackendサーバーにはHTTPサーバーがいらない
BackendサーバーにはHTTPサーバーがいらないので次のように@SpringBootApplicationがついたメインクラスの起動でWebアプリケーションを動かさないようにした。
|  |  | 
コード
今回試したコードはgithubに置いてあるので参考になれば嬉しい。
まとめ
- 理想の構成どおりにアプリケーションが組めた
- Spring5.0のkotlinサポートは魅力的であり実戦投入を検討したかったがgRPCとの相性が良くない印象であったが検証を経て問題ないことが確認できた
- いよいよSpring5.0の正式リリースに向けて整ってきている印象を受けた
- その他で利用しているSpringエコシステムの各種ライブラリとのSpring5.0/Spring Boot 2.0.0の相性が問題ないことを引き続き確認していき導入を検討していきたい
