Quarkus 2.2.0 で変更された RESTEasy Reactive のスレッド割当ルール

f:id:Naotsugu:20210901204009p:plain

RESTEasy Reactive とは

RESTEasy Reactive は Quarkus 1.11 で追加された拡張です。RESTEasyReactive では、 JAX-RS API を利用したまま、リクエストをイベントループ(Quarkus ではこれを I/Oスレッドと呼びます)で処理することでスループットを大幅に向上できます。

RESTEasy Reactive の拡張には以下のものが提供されています。

  • quarkus-resteasy-reactive
  • quarkus-resteasy-reactive-jackson
  • quarkus-resteasy-reactive-jsonb
  • quarkus-resteasy-reactive-links
  • quarkus-resteasy-reactive-qute


Quarkus 2.2.0 で変更されたスレッドの割当ルール

I/Oスレッドはノンブロッキングであり、1つのスレッドで複数のリクエストを処理することができます。これに対して、1つのリクエストに1つのスレッドを割り当てる場合には、(旧来からの)ワーカースレッドが使われます。

Quarkus 2.2.0 以前の RESTEasy Reactive では、デフォルトで全てのリクエストが I/Oスレッドで実行されました。ワーカースレッドを使う場合には、エンドポイントメソッドに @Blocking を指定する必要があり、特に Hibernate ORM などのブロッキングな操作を行う場合は、このアノテーションを明示的に追加する必要がありました。


Quarkus 2.2.0 からは、メソッドシグネチャに基づいて、I/Oスレッドまたはワーカースレッドが自動的に選択されるように変更されました。

戻り値の型がリアクティブな場合は、I/Oスレッドが利用され、それ以外の場合にはワーカースレットへディスパッチされます。つまり以下のような割当となります。

  • T method(…) ワーカースレッド
  • Uni<T> method(…) I/Oスレッド
  • CompletionStage<T> method(…) I/Oスレッド
  • Multi<T> method(…) I/Oスレッド
  • Publisher<T> method(…) I/Oスレッド

ただし、@Transactional CompletionStage<T> method(…) のように @Transactional でアノテートされていた場合は ワーカースレッド が利用されます。

これらの割当ポリシーは @Blocking@NonBlocking アノテーションを明示することで上書き変更することができます。

以下の例では、以前は I/Oスレッドで処理されていましたが、Quarkus 2.2.0 からはワーカースレッドで処理されるように変わりました。

package org.acme;

import javax.ws.rs.GET;
import javax.ws.rs.Path;

@Path("/hello")
public class GreetingResource {

   @GET
   public String hello() {
       return "Hello";
   }
}

I/O スレッドで処理するには @NonBlocking を付けるか、以下のような戻り値に変更します。

import io.smallrye.mutiny.Uni;
import javax.ws.rs.GET;
import javax.ws.rs.Path;

@Path("/hello")
public class GreetingResource {

   @GET
   public Uni<String> hello() {
       return Uni.createFrom().item("Hello");
   }
}


旧来からの Hibernate ORM を利用する場合は以下のようになり、これはワーカースレッドで処理されます。

import org.jboss.resteasy.reactive.RestQuery;
import javax.ws.rs.GET;
import javax.ws.rs.Path;

@Path("/fruit")
public class FruitResource {

   @GET
   public Fruit getFruit(@RestQuery String name) {
       return Fruit.find("name", name).firstResult();
   }
}

Hibernate Reactive を使った場合は、以下のようにすることで I/Oスレッドが使われます。

import io.smallrye.mutiny.Uni;
import org.jboss.resteasy.reactive.RestQuery;

import javax.ws.rs.GET;
import javax.ws.rs.Path;

@Path("/fruit")
public class FruitResource {

   @GET
   public Uni<Fruit> getFruit(@RestQuery String name) {
       return Fruit.find("name", name).firstResult();
   }
}


まとめ

Quarkus 2.2.0 から RESTEasy Reactive のスレッド割当ポリシーが変更されました。

I/Oスレッドが利用されるか、ワーカースレッドが利用されるかは、メソッドシグネチャにより自動的に判断されるようになりました。

旧来からの RESTEasy Reactive ユーザは注意してください。