yu-tarrrrの日記

完全に個人の趣味でその時々書きたいことを書く

Quarkus×KotlinでReactive programmingしてみる

はじめに

  • 今回はQuarkusでReactive programmingをしてみます
  • Springでは5系からweb-fluxというリアクティブ・プログラミングが実装されました
  • キーワードとしては、非同期・ノンブロッキング I.Oというのがあげられます
  • そのReactive programmingをQuarkusでも実装してみたいと思います

早速実装してみる

  • 前回までと同様に、今回もKotlin×Quarkusで実装します(package管理はGradleに任せます)

実装する前に

  • QuarkusのHTTPレイヤーについてちょっと調査してみる 参考

    • QuarkusのHTTPサポートはノンブロッキングでかつ非同期のエンジンに基づいている(Eclipse Vert.x and Netty)
    • 全てのリクエストはIO Threadによって制御されて、実装に基づいてServlet, Jax-RSといったWorker threadを使うのか、IO Threadであるreactive route を使うのかといった具合に処理されていく
    • なので、reactiveの実装の場合はノンブロッキングの実装になるということ

実装に戻る

まず、build.gradleに依存追加しておく

./gradlew addExtension --extensions="quarkus-vertx-web"
Starting a Gradle Daemon (subsequent builds will be faster)

> Task :addExtension
✅ Adding extension io.quarkus:quarkus-vertx-web

BUILD SUCCESSFUL in 6s
1 actionable task: 1 executed

あとは、@Routeアノテーションを対象のメソッドに付与してあげて、処理をよしなに書く。 因みに、@ApplicationScoped アノテーションを付与してあげることで、該当のクラスをアプリケーションの開始から終了までCDI Beanに登録することができる。付与しない場合は、 @javax.inject.Singleton が自動的に付与される。

import io.quarkus.vertx.web.Route;
import io.vertx.core.http.HttpMethod;
import io.vertx.ext.web.RoutingContext
import javax.enterprise.context.ApplicationScoped

@ApplicationScoped
class ReactiveSampleResource {

    @Route(path = "/greetings", methods = [HttpMethod.GET])
    fun hello(rc: RoutingContext) {
        rc.response().end("hello")
    }

}

また、リクエストパラメータを取得したい時はserver requestから取得できる。 因みに、@Routeの引数にパスを渡さずににコンパイルすることは可能ではあり、その場合はmethod名がパスとして認識され、そのパスで実際にリクエストすることが可能になる。

@Route(methods = [HttpMethod.GET])
    fun good(rc: RoutingContext) {
        val request = rc.request().params().get("name")
        rc.response().end("hello ${request}")
    }

最後にカスタムクラスを返却する場合は下記の通り。

import io.quarkus.vertx.web.Route
import io.vertx.core.http.HttpMethod
import io.vertx.core.json.Json
import io.vertx.ext.web.RoutingContext
import javax.enterprise.context.ApplicationScoped

@ApplicationScoped
class ReactiveSampleResource {

    @Route(path = "register", methods = [HttpMethod.GET])
    fun registerName(rc: RoutingContext) {
        val user = User(1, rc.request().params().get("name"))
        rc.response().end(Json.encodePrettily(user))
    }
}

class User(val id: Int, val name: String)
  • RoutingContextのresponseでendメソッドを呼ぶのですが、カスタムクラスのまま渡せないようなので、Jsonにencodeしてあげる必要がある。
  • ここらへんは、Quarkusの実装というより、vert.xの実装なので、ドキュメントを探す場合はvert.xを見に行った方が良さそう。
  • また、 ./gradlew quarkusDev で起動してた時に、hot reloadが走るのですが、その時にうまくリコンパイルされずに怒られたので、アプリケーションごと立ち上げ直して解決させた。

まとめ

  • reactive programmingする場合は,vert.x をちゃんと勉強した方が良さそう