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响应式架构 中所描述的,下面,Quarkus将使用Vert.x。

Quarkus Reactive Core

Quarkus应用程序能够访问和使用Vert.x APIs。

该指南介绍如何使用以下方法构建 Quarkus 应用程序:

  • 被管理的 Vert.x 实例

  • Vert.x 事件总线

  • Vert.x 网络客户端

这是一项介绍性的指南。 Vert.x参考指南 涵盖了更多的高级功能,例如 verticles 和 本地传输 (native transports) 。

架构

我们将构建一个简单应用程序 它包含四个暴露出的端点:

  1. /vertx/lorem 返回一个小文件的内容

  2. /vertx/book 返回一个大文件的内容(一本书)

  3. /vertx/hello 使用Vert.x事件总线来生成响应

  4. /vertx/web 使用Vert.x Web客户端从维基百科中检索数据

Architecture of the Vert.x guide

解决方案

我们建议你按照下一节的说明逐步创建应用程序。然而,你也可以直接转到已完成的示例。

克隆 Git 仓库: git clone https://github.com/quarkusio/quarkus-quickstarts.git ,或者下载一个 存档

The solution is located in the vertx-quickstart directory.

Mutiny

该指南使用Mutiny API。如果你不熟悉Mutiny,请查看 Mutiny - 一个直观的、响应式的编程库

引导启动该应用程序

Click on this link to configure your application. It selected a few extensions:

  • rest-jackson, which also brings rest. We are going to use it to expose our HTTP endpoints.

  • vertx ,它提供了对底层管理的Vert.x的访问

点击 生成你的应用程序 按钮,下载压缩文件并解压。然后,在你喜欢的编辑器中打开该项目。

如果你打开生成的构建文件,你可以看到以下选定的扩展:

pom.xml
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-rest-jackson</artifactId>
</dependency>
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-vertx</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-rest-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核心API

被注入的Vert.x实例提供了一组你可以使用的API。我们在本节中要使用的是Vert.x文件系统。它提供了一个非阻塞的API来访问文件。

In the src/main/resources directory, create a lorem.txt file with the following content:

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 一旦文件被读取,其内容就被存储在一个内存缓冲区内。我们将这个缓冲区的内容转换为一个字符串。

在终端中,切换到项目根目录下 运行以下命令:

CLI
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

在另外一个终端中,运行:

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

你应该会在控制台中看到打印的文件内容。

Quarkus提供了其他方式来处理静态文件。这是专为该指南制作的一个例子。

使用Vert.x流的功能

读取文件并将内容存储在内存中适用于小文件,但不适用于大文件。在本节中,我们将带领你了解如何使用 Vert.x 流功能。

首先,下载 《战争与和平 》,并将其存储在 src/main/resources/book.txt 。这是一个3.2Mb的文件,虽然不是很大,但说明了流的用途。这一次,我们将不会把文件的内容堆积在内存中并一次性写入,而是逐块读取,并把这些块逐一写入到HTTP响应中。

因此,你的项目中应该包含以下文件:

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

Add the following imports to the src/main/java/org/acme/VertxResource.java file:

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 对于每个块,我们生成一个字符串
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.”
...

使用事件总线

Vert.x 的核心特点之一是 事件总线。它为你的应用程序提供了基于消息的主干。因此,你可以使用异步消息传递的方式在组件之间进行交互,并且将你的组件解耦。您可以向单个消费者发送消息,或分派给多个消费者,亦或是实现请求-响应的交互,在其中发送消息(请求)并期许响应。这是我们将在本节中使用的内容。我们的 VertxResource 将向问候地址发送一条包含姓名的消息。另一个组件将会接收到这条消息并生成 "hello $name" 的响应。 VertxResource 将收到响应并将其作为 HTTP 响应进行返回。

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

现在,我们需要另一侧: 接收姓名并回复的组件。 使用以下内容创建 src/main/java/org/acme/GreetingService.java 文件:

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

让我们试试这个。 在终端中,运行以下命令:

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

你应该会收到预期的 Hello bob 消息。

使用Vert.x客户端

到目前为止,我们已经使用了 Vert.x 核心 API。 Vert.x 提供的不仅仅是这些。它提供了一个广阔的生态系统。在本节中,我们将会带你了解如何使用 Vert.x Web 客户端 - 一个响应式 HTTP 客户端。

注意,部分 Quarkus 扩展正在封装 Vert.x 客户端并为您管理它们。如响应式数据源、Redis、邮件等…​ 但Web 客户端,并不在此行列之内。

请记住,在本指南的开头,我们将 smallrye-mutiny-vertx-web-client 作为依赖项添加到了我们的 pom.xml 文件中。现在,是时候使用它了。

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

现在,让我们实现一个新的 HTTP 端点,它通过查询 Wikipedia API 以检索不同语言有关 Quarkus 的页面。 将以下方法添加到 VertxResource 类中:

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.

接着,使用以下命令请求该端点:

> 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"}]

该响应表明,除了英文页面,维基百科上还有关于 Quarkus 的德文和法文页面。

Executing Asynchronous Code From a Blocking Thread

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!

It is also possible to subscribe to a supplied io.smallrye.mutiny.Multi on a Vert.x duplicated context. In this case, the current thread is not blocked and the supplied subscription logic is used to consume the events.

void onStart(@Observes StartupEvent event, ExternalService service) {
   VertxContextSupport.subscribeWith(() -> service.getFoos(), foo -> {
     // do something useful with foo
   });
}

进一步探索

本指南介绍了如何在 Quarkus 应用程序中使用 Vert.x API。这只是一个简短的概述。如果你想了解更多信息,请查看 Quarkus 中有关 Vert.x 的参考指南

正如我们所见,事件总线是 Vert.x 应用程序之间的连接桥梁。 Quarkus 集成了它,因此不同的bean之间能够以异步消息的方式进行交互。这部分内容包含在 事件总线文档 中。

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