The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

QuarkusアプリケーションからのEclipse Vert.x APIの使用

Vert.xは、リアクティブなアプリケーションを構築するためのツールキットです。 Quarkus Reactive Architectureに記載されているように、QuarkusではVert.xを下地に使用しています。

Quarkus Reactive Core

QuarkusのアプリケーションはVert.xのAPIにアクセスして使用することができます。

このガイドでは、Quarkusのアプリケーションで次を使って構築する方法を紹介します。

  • Vert.xのマネージドインスタンス

  • Vert.x イベントバス

  • Vert.x Webクライアント

これは入門ガイドです。 Vert.xリファレンスガイドでは、バーティクルやネイティブトランスポートなど、より高度な機能について説明しています。

アーキテクチャ

ここでは、4つのHTTPエンドポイントを公開するシンプルなアプリケーションを作成します。

  1. /vertx/lorem 小さなファイルからコンテンツを返す

  2. /vertx/book 大きなファイル(本)からコンテンツを返す

  3. /vertx/hello は、Vert.x イベントバスを使用してレスポンスを生成します

  4. /vertx/web は、Vert.x Web クライアントを使用して Wikipedia からデータを取得します

Vert.xガイドのアーキテクチャ

ソリューション

次のセクションで紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。ただし、完成した例にそのまま進んでも構いません。

Gitリポジトリをクローン: git clone https://github.com/quarkusio/quarkus-quickstarts.git 、または archive をダウンロードする。

The solution is located in the vertx-quickstart directory.

Mutiny

このガイドでは、Mutiny APIを使用しています。Mutinyに慣れていない方は、 Mutiny - 直感的なリアクティブプログラミングライブラリをご覧ください。

アプリケーションの起動

このリンクをクリックして、アプリケーションを設定してください。いくつかのエクステンションが選択されます。

  • resteasy-reactive-jackson 。これは resteasy-reactive も持ち込みます。これを使って、HTTPエンドポイントを公開します。

  • vertx 、下地となるマネージドVert.xへのアクセスを提供します。

Generate your application ボタンをクリックし、ZIPファイルをダウンロードして解凍します。その後、お好きなIDEでプロジェクトを開いてください。

生成されたビルドファイルを開くと、選択されたエクステンションが表示されます:

pom.xml
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-vertx</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-resteasy-reactive-jackson")
implementation("io.quarkus:quarkus-vertx")

ビルドファイルの中で、以下の依存関係を追加してください。

pom.xml
<dependency>
  <groupId>io.smallrye.reactive</groupId>
  <artifactId>smallrye-mutiny-vertx-web-client</artifactId>
</dependency>
build.gradle
implementation("io.smallrye.reactive:smallrye-mutiny-vertx-web-client")

この依存関係は、Vert.x Web クライアントを提供し、 /web エンドポイントの実装に使用されます。

Vert.xマネージドインスタンスへのアクセス

src/main/java/org/acme/VertxResource.java ファイルを作成します。このファイルには、HTTPエンドポイントが含まれます。

このファイルに、以下のコードをコピーします。

package org.acme;

import io.smallrye.mutiny.Uni;
import io.vertx.mutiny.core.Vertx;

import java.nio.charset.StandardCharsets;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@Path("/vertx")                        (1)
public class VertxResource {

    private final Vertx vertx;

    @Inject                             (2)
    public VertxResource(Vertx vertx) { (3)
        this.vertx = vertx;             (4)
    }
}
1 ルートHTTPパスを宣言します。
2 コンストラクタ・インジェクションを使用して、マネージドVert.xインスタンスを受け取ります。フィールド・インジェクションも有効です。
3 コンストラクタのパラメータとしてVert.xインスタンスを受け取ります。
4 マネージドVert.xインスタンスをフィールドに格納します。

これで、エンドポイントの実装を始めることができます。

Vert.x Core APIの使用

インジェクションされたVert.xインスタンスは、利用可能なAPIのセットを提供します。このセクションで使用するのはVert.x File Systemです。これは、ファイルにアクセスするためのノンブロッキングAPIを提供します。

src/main/resource ディレクトリに lorem.txt ファイルを次の内容で作成します。

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

そして、 VertxResource ファイルに以下のメソッドを追加します。

@GET                                                                                   (1)
@Path("/lorem")
public Uni<String> readShortFile() {                                                   (2)
    return vertx.fileSystem().readFile("lorem.txt")                                    (3)
            .onItem().transform(content -> content.toString(StandardCharsets.UTF_8));  (4)
}
1 このエンドポイントは、パス /lorem に対する HTTP GET リクエストを処理します(そのため、フルパスは vertx/lorem となります)。
2 Vert.x APIは非同期なので、このメソッドは Uni を返却します。Uniで表される非同期操作が完了すると、その内容がHTTPレスポンスに書き込まれます。
3 Vert.xのファイルシステムAPIを使用して、作成されたファイルを読み取ります。
4 ファイルが読み込まれると、その内容がメモリ内のバッファに格納されます。このバッファをStringに変換します。

ターミナルで、プロジェクトのルートに移動して次を実行します。

コマンドラインインタフェース
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

別の端末で、次を実行します。

> curl http://localhost:8080/vertx/lorem

コンソールにファイルの内容が表示されるはずです。

Quarkusは、静的ファイルを提供する他の方法を提供しています。これはガイドのために作られた例です。

Vert.x stream の能力の使用

ファイルを読み込んで内容をメモリに保存する方法は、小さなファイルには有効ですが、大きなファイルには使えません。このセクションでは、Vert.xのストリーミング機能をどのように使用するかを説明します。

まず、 戦争と平和をダウンロードして、 src/main/resources/book.txt に保存します。これは3.2Mbのファイルで、巨大ではありませんが、ストリームの目的を説明しています。今回は、ファイルの内容をメモリに蓄積して一括して書き込むのではなく、チャンクごとに読み込んで、そのチャンクを1つずつHTTPレスポンスに書き込んでいきます。

そこで、プロジェクト内に以下のファイルを用意しておきます。

.
├── mvnw
├── mvnw.cmd
├── pom.xml
├── README.md
├── src
│  └── main
│     ├── docker
│     │  ├── ...
│     ├── java
│     │  └── org
│     │     └── acme
│     │        └── VertxResource.java
│     └── resources
│        ├── application.properties
│        ├── book.txt
│        └── lorem.txt

src/main/java/org/acme/VertxResource.java ファイルに次のimportを追加します:

import io.smallrye.mutiny.Multi;
import io.vertx.core.file.OpenOptions;

VertxResource クラスに以下のメソッドを追加します。

@GET
@Path("/book")
public Multi<String> readLargeFile() {                                               (1)
    return vertx.fileSystem().open("book.txt",                                       (2)
                    new OpenOptions().setRead(true)
            )
            .onItem().transformToMulti(file -> file.toMulti())                       (3)
            .onItem().transform(content -> content.toString(StandardCharsets.UTF_8) (4)
                    + "\n------------\n");                                           (5)
}
1 今回は、チャンクをストリーミングしたいので、Multiを返します。
2 open メソッドを使ってファイルを開きます。このメソッドは Uni<AsyncFile> を返却します。
3 ファイルが開かれると、チャンクを含む Multi が取得できます。
4 各チャンクは、String を生成します。
5 レスポンス内のチャンクを視覚的に確認するために、セパレーターを追加します。

そして、ターミナルで、次を実行します。

> curl http://localhost:8080/vertx/book

ブックの内容を取得します。出力には次のようなセパレータが表示されるはずです。

...
The little princess had also left the tea table and followed Hélène.

“Wait a moment, I’ll get my work.... Now then, what
------------
 are you
thinking of?” she went on, turning to Prince Hippolyte. “Fetch me my
workbag.”
...

イベントバスの使用

One of the core features of Vert.x is the event bus. It provides a message-based backbone to your application. So, you can have components interacting using asynchronous message passing, and so decouple your components. You can send a message to a single consumer, or dispatch to multiple consumers, or implement a request-reply interaction, where you send a message (request) and expect a response. This is what we are going to use in this section. Our VertxResource will send a message containing a name to the greetings address. Another component will receive the message and produce the "hello $name" response. The VertxResource will receive the response and return it as the HTTP response.

So, first, add the following imports to the src/main/java/org/acme/VertxResource.java file:

import io.vertx.mutiny.core.eventbus.EventBus; import jakarta.ws.rs.QueryParam;

Next, let’s extend our VertxResource class with the following code:

@Inject
EventBus bus;                                                   (1)

@GET
@Path("/hello")
public Uni<String> hello(@QueryParam("name") String name) {     (2)
    return bus.<String>request("greetings", name)               (3)
            .onItem().transform(response -> response.body());   (4)
}
1 Inject the event bus. Alternatively you can use vertx.eventBus().
2 We receive a name as a query parameter
3 We use the request method to initiate the request-reply interaction. We send the name to the "greetings" address.
4 When the response is received, we extract the body and return it as the HTTP response

Now, we need the other side: the component receiving the name and replying. Create the src/main/java/org/acme/GreetingService.java file with the following content:

package org.acme;

import io.quarkus.vertx.ConsumeEvent;

import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped                          (1)
public class GreetingService {

    @ConsumeEvent("greetings")              (2)
    public String hello(String name) {      (3)
        return "Hello " + name;             (4)
    }
}
1 Declaring a CDI Bean in the Application scope. Quarkus will create a single instance of this class.
2 Use the @ConsumeEvent annotation to declare a consumer. It is possible to use the Vert.x API directly too.
3 Receive the message payload as a method parameter. The returned object will be the reply.
4 Return the response. This response is sent back to the VertxResource class

Let’s try this. In a terminal, run:

> curl "http://localhost:8080/vertx/hello?name=bob"

You should get the expected Hello bob message back.

Vert.x クライアントの使用

So far, we have used the Vert.x Core API. Vert.x offers much more. It provides a vast ecosystem. In this section, we will see how you can use the Vert.x Web Client, a reactive HTTP client.

Note that some Quarkus extensions are wrapping Vert.x clients and manage them for you. That’s the case for the reactive data sources, Redis, mail…​ That’s not the case with the Web Client.

Remember, at the beginning of the guide, we added the smallrye-mutiny-vertx-web-client dependency to our pom.xml file. It’s now time to use it.

First, add the following imports to the src/main/java/org/acme/VertxResource.java file:

import io.vertx.core.json.JsonArray; import io.vertx.mutiny.ext.web.client.HttpResponse; import io.vertx.mutiny.ext.web.client.WebClient;

Next, we need to create an instance of WebClient. Extend the VertxResource class with the client field and the creation of the web client in the constructor:

private final Vertx vertx;
private final WebClient client;            (1)

@Inject
public VertxResource(Vertx vertx) {
    this.vertx = vertx;
    this.client = WebClient.create(vertx); (2)
}
1 Store the WebClient, so we will be able to use it in our HTTP endpoint
2 Create the WebClient. Be sure to use the io.vertx.mutiny.ext.web.client.WebClient class

Let’s now implement a new HTTP endpoint that queries the Wikipedia API to retrieve the pages about Quarkus in the different languages. Add the following method to the VertxResource class:

private static final String URL = "https://en.wikipedia.org/w/api.php?action=parse&page=Quarkus&format=json&prop=langlinks";

@GET
@Path("/web")
public Uni<JsonArray> retrieveDataFromWikipedia() {                     (1)
    return client.getAbs(URL).send()                                    (2)
            .onItem().transform(HttpResponse::bodyAsJsonObject)         (3)
            .onItem().transform(json -> json.getJsonObject("parse")     (4)
                                        .getJsonArray("langlinks"));
}
1 This endpoint returns a JSON Array. Vert.x provides a convenient way to manipulate JSON Object and Array. More details about these in the reference guide.
2 Send a GET request to the Wikipedia API
3 Once the response is received, extract it as a JSON Object
4 Extract the langlinks array from the response.

Then, invoke the endpoint using:

> curl http://localhost:8080/vertx/web
[{"lang":"de", "url", :"https://de.wikipedia.org/wiki/Quarkus", "langname", :"German", "autonym", :"Deutsch", "*", :"Quarkus"}, {"lang":"fr", "url", :"https://fr.wikipedia.org/wiki/Quarkus", "langname", :"French", "autonym", :"français", "*", :"Quarkus"}]

The response indicates that, in addition to the English page, there are a German and a French page about Quarkus on wikipedia.

ブロッキングスレッドから非同期コードを実行

Sometimes it’s necessary to execute an asynchronous code from a blocking thread. Specifically, to execute the code on a Vert.x thread with an isolated/duplicated Vert.x context. A typical example is an asynchronous code that needs to leverage the Hibernate Reactive API during application startup. Quarkus provides the VertxContextSupport#subscribeAndAwait() method which subscribes to the supplied io.smallrye.mutiny.Uni on a Vert.x duplicated context, then blocks the current thread and waits for the result.

void onStart(@Observes StartupEvent event, Mutiny.SessionFactory sf) {
   VertxContextSupport.subscribeAndAwait(() -> {
      return sf.withTransaction(session -> session.persist(new Person()));
   });
}
If necessary, the CDI request context is activated during execution of the asynchronous code.
VertxContextSupport#subscribeAndAwait() must not be called on an event loop!

さらに詳しく

This guide introduced how you can use Vert.x APIs from a Quarkus application. It’s just a brief overview. If you want to know more, check the reference guide about Vert.x in Quarkus.

As we have seen, the event bus is the connecting tissue of Vert.x applications. Quarkus integrates it so different beans can interact with asynchronous messages. This part is covered in the event bus documentation.

Learn how to implement highly performant, low-overhead database applications on Quarkus with the Reactive SQL Clients.