Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up
for free
grpc-kotlinはどこまでKotlin化できるのか?
Takehata Naoto
June 17, 2020
Programming
0
260
grpc-kotlinはどこまでKotlin化できるのか?
2020年6月17日(水) 「集まれKotlin好き!Kolint愛好会 vol.22 @オンライン」の談義資料です。
Takehata Naoto
June 17, 2020
Tweet
Share
More Decks by Takehata Naoto
See All by Takehata Naoto
n_takehata
0
75
n_takehata
1
6.5k
n_takehata
2
720
n_takehata
1
190
n_takehata
2
550
n_takehata
0
660
n_takehata
0
450
n_takehata
1
380
n_takehata
1
1.7k
Other Decks in Programming
See All in Programming
kazaman97
0
190
takahi5
0
240
nanimonodemonai
2
1.4k
hr01
0
1.6k
itosho525
1
370
siketyan
1
120
hanakla
2
3k
o0h
PRO
1
1.5k
chichou
1
850
taoshotaro
1
370
ajstarks
2
550
itosho525
0
150
Featured
See All Featured
marcelosomers
220
15k
malarkey
192
8.6k
cromwellryan
101
5.9k
searls
204
35k
samanthasiow
56
6.3k
dougneiner
55
5.4k
iamctodd
17
1.8k
bryan
30
3.3k
garrettdimon
287
110k
jacobian
255
20k
schacon
145
6.6k
eileencodes
113
25k
Transcript
grpc-kotlinはどこまでKotlin化できるのか? 2020年6月17日 Kotlin愛好会 vol.22 竹端 尚人
自己紹介
概要 氏名:竹端 尚人 Twitter:@n_takehata ・サーバーサイドKotlin ・(少し前まで)スマートフォンゲーム開発 ・今月転職して新しい会社で???を開発
登壇、執筆など ・CEDEC 2018登壇 ・Software Design 2019年2〜4月号で短期連載 ・Swift/Kotlin愛好会で技術書典執筆 https://booth.pm/ja/items/1315478
今日話すこと
grpc-java -> gprc-kotlin • 今年の4月にgrpc-kotlinがリリース • これまでKotlinでgRPCを使うにはgrpc-javaを使用していたが、 待望のKotlinのライブラリが登場 • ただし、まだ全てをKotlin化できるわけではない
grpc-kotlinを使うGradleの依存関係(抜粋) implementation "io.grpc:grpc-kotlin-stub:$grpc_kotlin_version" implementation "com.google.protobuf:protobuf-java:$protobuf_version" implementation "com.google.protobuf:protobuf-java-util:$protobuf_version" implementation "io.grpc:grpc-netty-shaded:$grpc_version" implementation
"io.grpc:grpc-protobuf:$grpc_version" implementation "io.grpc:grpc-stub:$grpc_version" Javaのライブラリも併せて必要
コード生成のGradleタスク(抜粋) Javaのコード生成も必要 plugins { grpc { artifact = "io.grpc:protoc-gen-grpc-java:$grpc_version" }
grpckt { artifact = "io.grpc:protoc-gen-grpc-kotlin:$grpc_kotlin_version" } }
gRPCの実装を どこまでKotlin化できるのかを紹介します
アジェンダ 1.どこをKotlin化できるのか? 2.Service 3.Stub 4.Javaのままの箇所 5.grpc-spring-boot-starterと組み合わせて使う
1.どこをKotlin化できるのか?
gRPC実装時の主な要素 • サーバー • Service • ServerInterceptor • クライアント •
Channel • Stub • ClientInterceptor • 共通 • message
gRPC実装時の主な要素 • サーバー • Service • ServerInterceptor • クライアント •
Channel • Stub • ClientInterceptor • 共通 • message
Service、Stubの部分がKotlin化できる
2.Service
実装比較
grpc-javaでの実装(抜粋) class GreeterImpl : GreeterGrpc.GreeterImplBase() { override fun sayHello(req: HelloRequest,
responseObserver: StreamObserver<HelloReply>) { val reply = HelloReply.newBuilder().setMessage("Hello ${req.name}").build() responseObserver.onNext(reply) responseObserver.onCompleted() } } Observerパターンで実装されている
grpc-kotlinでの実装(抜粋) class HelloWorldService : GreeterGrpcKt.GreeterCoroutineImplBase() { override suspend fun sayHello(request:
HelloRequest) = HelloReply .newBuilder() .setMessage("Hello ${request.name}") .build() } ・親クラスをKotlin化したものに変更 ・suspend関数に変更 ・Observerを使わなくなった
ただし
CoroutineServiceの親クラス(抜粋) Javaのクラスを継承している(Javaに依存している) abstract class AbstractCoroutineServerImpl( /** The context in which
to run server coroutines. */ val context: CoroutineContext = EmptyCoroutineContext ) : BindableService {
3.Stub
実装比較
grpc-javaでの実装(抜粋) fun greet(name: String) { val blockingStub: GreeterGrpc.GreeterBlockingStub = GreeterGrpc.newBlockingStub(channel)
val request = HelloRequest.newBuilder().setName(name).build() val response: HelloReply = blockingStub.sayHello(request) logger.info("Greeting: ${response.message}") } Stubを生成して実行
grpc-kotlinでの実装(抜粋) suspend fun greet(name: String) = coroutineScope { val stub:
GreeterCoroutineStub = GreeterCoroutineStub(channel) val request = HelloRequest.newBuilder().setName(name).build() val response = async { stub.sayHello(request) } println("Received: ${response.await().message}") } ・Kotlin化されたStubを使う ・Stubの関数もsuspend関数になっているため、非同期で実行する
ただし
Stubの親クラス abstract class AbstractCoroutineStub<S: AbstractCoroutineStub<S>>( channel: Channel, callOptions: CallOptions =
CallOptions.DEFAULT ): AbstractStub<S>(channel, callOptions) Serviceと同様Javaのクラスを継承している(Javaに依存している)
4.Javaのままの箇所
Javaのままの箇所 • ServerInterceptor • ClientInterceptor • Channel • message
ServerInterceptor @GRpcGlobalInterceptor @Order(10) class LoggingInterceptor : ServerInterceptor { companion object
{ private val log = LoggerFactory.getLogger(LoggingInterceptor::class.java) } override fun <ReqT : Any, RespT : Any> interceptCall(call: ServerCall<ReqT, RespT>, headers: Metadata, next: ServerCallHandler<ReqT, RespT>): ServerCall.Listener<ReqT> { log.info("method name=${call.methodDescriptor.fullMethodName}") return next.startCall(call, headers) } }
ClientIterceptor class MetadataClientInterceptor : ClientInterceptor { companion object { private
val log = LoggerFactory.getLogger(MetadataClientInterceptor::class.java) } override fun <ReqT, RespT> interceptCall(method: MethodDescriptor<ReqT, RespT>?, callOptions: CallOptions?, next: Channel): ClientCall<ReqT, RespT>? { return object : SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) { override fun start(responseListener: Listener<RespT>?, headers: Metadata) { headers.put(Metadata.Key.of(“example”, Metadata.ASCII_STRING_MARSHALLER), “Example”) // ・・・
Channel、message suspend fun greet(name: String) = coroutineScope { val channel
= ManagedChannelBuilder.forAddress("localhost", 6565) .usePlaintext().build() val stub: GreeterCoroutineStub = GreeterCoroutineStub(channel) val request = HelloRequest.newBuilder().setName(name).build() val response = async { stub.sayHello(request) } println("Received: ${response.await().message}") }
messageをもしデータクラスにできたら・・・ suspend fun greet(name: String) = coroutineScope { val channel
= ManagedChannelBuilder.forAddress("localhost", 6565) .usePlaintext().build() val stub: GreeterCoroutineStub = GreeterCoroutineStub(channel) val request = HelloRequest (name) val response = async { stub.sayHello(request) } println("Received: ${response.await().message}") }
5.grpc-spring-boot-starterと組み合わせて使う
Gradleの依存関係
grpc-kotlinを使うGradleの依存関係(再掲) implementation "io.grpc:grpc-kotlin-stub:$grpc_kotlin_version" implementation "com.google.protobuf:protobuf-java:$protobuf_version" implementation "com.google.protobuf:protobuf-java-util:$protobuf_version" implementation "io.grpc:grpc-netty-shaded:$grpc_version" implementation
"io.grpc:grpc-protobuf:$grpc_version" implementation "io.grpc:grpc-stub:$grpc_version"
spring-boot-starterを使う場合の依存関係(抜粋) implementation "org.springframework.boot:spring-boot-starter-webflux” implementation "io.github.lognet:grpc-spring-boot-starter:$grpc_spring_boot_version" implementation "io.grpc:grpc-kotlin-stub:$grpc_kotlin_version”
サーバーの実装比較
grpc-javaでの実装(抜粋) @GRpcService(interceptors = [MessageInterceptor::class]) class ExampleGrpcService : ExampleGrpc.ExampleImplBase() { override
fun createMessage(request: CreateMessageRequest, responseObserver: StreamObserver<CreateMessageResponse>) { // ・・・
grpc-kotlinでの実装(抜粋) @GRpcService(interceptors = [MessageInterceptor::class]) class ExampleGrpcService : ExampleGrpcKt.ExampleCoroutineImplBase() { override
suspend fun createMessage(request: CreateMessageRequest): CreateMessageResponse { // ・・・ もともとの実装にgrpc-kotlinを組み込んだ形
クライアントの実装比較 (Controllerからの呼び出しを例に)
grpc-javaでの実装(抜粋) @RequestMapping("/createmessage") fun createMessage(@RequestBody request: RestCreateMessageRequest): String { val createMessageRequest
= CreateMessageRequest.newBuilder().setName(request.name).build() val channel = ManagedChannelBuilder.forAddress("localhost", 6565) .usePlaintext().build() val stub = ExampleGrpc.newBlockingStub(channel) val response = stub.createMessage(createMessageRequest) return response.message.text }
grpc-kotlinでの実装(抜粋) @PostMapping("/createmessage") suspend fun createMessage(@RequestBody request: RestCreateMessageRequest): String { val
createMessageRequest = CreateMessageRequest.newBuilder().setName(request.name).build() val channel = ManagedChannelBuilder.forAddress("localhost", 6565) .usePlaintext().build() val stub = ExampleGrpcKt.ExampleCoroutineStub(channel) val response = async { stub.createMessage(createMessageRequest) } return response.await().message.text }
• Stubの関数がsuspend関数なので、Controllerまでsuspendが連鎖する • 通常のSpring BootではControllerをsuspend関数にできず、 runBlockingなどする必要がある
spring-boot-starterを使う場合の依存関係(抜粋) implementation "org.springframework.boot:spring-boot-starter-webflux” implementation "io.github.lognet:grpc-spring-boot-starter:$grpc_spring_boot_version" implementation "io.grpc:grpc-kotlin-stub:$grpc_kotlin_version” Spring WebFluxを使うと非同期I/Oが実現できる
KotlinでSpring Boot使うなら WebFluxにしたい
まとめ
• Service、Stubに関してはKotlin化できる • ただし親クラスがJavaだったり、Javaのライブラリを呼んでいたりと依存 はしている • spring-boot-starterとの組み合わせは問題なさそう
https://blog.takehata-engineer.com/entry/change-to-kotlin- by-grpc-kotlin こちらのブログもご御覧ください grpc-kotlinはgRPCの実装をどこまでKotlin化できるのか? grpc-kotlinをgrpc-spring-boot-starterと組み合わせて使う https://blog.takehata-engineer.com/entry/grpc-kotlin-with- grpc-spring-boot-starter
ご清聴ありがとうございました