在Quarkus应用程序中使用Eclipse Vert.x API
Vert.x 是一个用于构建响应式应用程序的工具集。正如 Quarkus响应式架构 中所描述的,下面,Quarkus将使用Vert.x。

Quarkus应用程序能够访问和使用Vert.x APIs。
该指南介绍如何使用以下方法构建 Quarkus 应用程序:
-
被管理的 Vert.x 实例
-
Vert.x 事件总线
-
Vert.x 网络客户端
这是一项介绍性的指南。 Vert.x参考指南 涵盖了更多的高级功能,例如 verticles 和 本地传输 (native transports) 。
架构
我们将构建一个简单应用程序 它包含四个暴露出的端点:
-
/vertx/lorem
返回一个小文件的内容 -
/vertx/book
返回一个大文件的内容(一本书) -
/vertx/hello
使用Vert.x事件总线来生成响应 -
/vertx/web
使用Vert.x Web客户端从维基百科中检索数据

解决方案
我们建议你按照下一节的说明逐步创建应用程序。然而,你也可以直接转到已完成的示例。
克隆 Git 仓库: git clone https://github.com/quarkusio/quarkus-quickstarts.git
,或者下载一个 存档。
The solution is located in the vertx-quickstart
directory.
Mutiny
该指南使用Mutiny API。如果你不熟悉Mutiny,请查看 Mutiny - 一个直观的、响应式的编程库 。 |
引导启动该应用程序
点击 这个链接 来配置你的应用程序。它选择了几个扩展:
-
resteasy-reactive-jackson
,它也带来了resteasy-reactive
。我们将使用它来暴露出我们的HTTP端点。 -
vertx
,它提供了对底层管理的Vert.x的访问
点击 生成你的应用程序
按钮,下载压缩文件并解压。然后,在你喜欢的编辑器中打开该项目。
如果你打开生成的构建文件,你可以看到以下选定的扩展:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx</artifactId>
</dependency>
implementation("io.quarkus:quarkus-resteasy-reactive-jackson")
implementation("io.quarkus:quarkus-vertx")
在你的构建文件中,添加以下依赖项:
<dependency>
<groupId>io.smallrye.reactive</groupId>
<artifactId>smallrye-mutiny-vertx-web-client</artifactId>
</dependency>
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来访问文件。
在 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 | 一旦文件被读取,其内容就被存储在一个内存缓冲区内。我们将这个缓冲区的内容转换为一个字符串。 |
在终端中,切换到项目根目录下 运行以下命令:
quarkus dev
./mvnw quarkus:dev
./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 | 注入事件总线。或者你可以使用 vertx.eventBus() |
2 | 我们接收到一个姓名作为查询参数 |
3 | 我们使用 request 方法来发起请求-回复的交互。我们将姓名发送到 "greetings" 地址 |
4 | 收到响应后,我们提取正文并将其作为 HTTP 响应进行返回 |
现在,我们需要另一侧: 接收姓名并回复的组件。
使用以下内容创建 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 | 以 Application scope 声明一个 CDI Bean。 Quarkus 将会为此类创建一个单实例对象。 |
2 | 使用 @ConsumeEvent 注解声明一个消费者. 你也可以直接使用 Vert.x API directly 。 |
3 | 接收消息负载作为方法的参数。返回的对象将会被作为回复。 |
4 | 返回该响应。该响应将会被发送回 VertxResource 类 |
让我们试试这个。 在终端中,运行以下命令:
> 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 | 存储 WebClient ,以便我们在 HTTP 端点中使用它 |
2 | 创建 WebClient 。确保使用 io.vertx.mutiny.ext.web.client.WebClient 类 |
现在,让我们实现一个新的 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 | 该端点返回一个 JSON 数组。Vert.x 提供了一种便捷的方式来操作 JSON 对象和数组。 有关这些的更多详细信息,请参阅 参考指南 |
2 | 向 Wikipedia API 发送 GET 请求 |
3 | 收到响应后,将其提取为 JSON 对象 |
4 | 从响应中提取 langlinks 数组 |
接着,使用以下命令请求该端点:
> 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之间能够以异步消息的方式进行交互。这部分内容包含在 事件总线文档 中。
了解如何使用 响应式SQL客户端 在 Quarkus 上实现高性能、低开销的数据库应用程序。